fix: consul catalog constraints.

This commit is contained in:
Ludovic Fernandez 2019-11-29 17:16:05 +01:00 committed by Traefiker Bot
parent a99673122e
commit cf1ace3a73
13 changed files with 332 additions and 126 deletions

View file

@ -1,11 +1,11 @@
# Traefik & Consul Catalog
A Story of Labels, Services & Containers
A Story of Tags, Services & Instances
{: .subtitle }
![Consul Catalog](../assets/img/providers/consul.png)
Attach labels to your services and let Traefik do the rest!
Attach tags to your services and let Traefik do the rest!
## Configuration Examples
@ -26,10 +26,9 @@ Attach labels to your services and let Traefik do the rest!
--providers.consulcatalog=true
```
Attaching labels to services
Attaching tags to services
```yaml
labels:
- traefik.http.services.my-service.rule=Host(`mydomain.com`)
```
@ -65,27 +64,27 @@ Defines the polling interval.
### `prefix`
_Optional, Default=/latest_
_required, Default="traefik"_
```toml tab="File (TOML)"
[providers.consulCatalog]
prefix = "/test"
prefix = "test"
# ...
```
```yaml tab="File (YAML)"
providers:
consulCatalog:
prefix: /test
prefix: test
# ...
```
```bash tab="CLI"
--providers.consulcatalog.prefix=/test
--providers.consulcatalog.prefix=test
# ...
```
Prefix used for accessing the Consul service metadata.
The prefix for Consul Catalog tags defining traefik labels.
### `requireConsistent`
@ -161,7 +160,7 @@ Use local agent caching for catalog reads.
### `endpoint`
Defines Consul server endpoint.
Defines the Consul server endpoint.
#### `address`
@ -504,7 +503,7 @@ providers:
```
Expose Consul Catalog services by default in Traefik.
If set to false, services that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
If set to false, services that don't have a `traefik.enable=true` tag will be ignored from the resulting routing configuration.
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
@ -532,13 +531,13 @@ providers:
The default host rule for all services.
For a given container if no routing rule was defined by a label, it is defined by this defaultRule instead.
For a given service if no routing rule was defined by a tag, it is defined by this defaultRule instead.
It must be a valid [Go template](https://golang.org/pkg/text/template/),
augmented with the [sprig template functions](http://masterminds.github.io/sprig/).
The service name can be accessed as the `Name` identifier,
and the template has access to all the labels defined on this container.
and the template has access to all the labels (i.e. tags beginning with the `prefix`) defined on this service.
This option can be overridden on a container basis with the `traefik.http.routers.Router1.rule` label.
The option can be overridden on an instance basis with the `traefik.http.routers.{name-of-your-choice}.rule` tag.
### `constraints`
@ -546,58 +545,59 @@ _Optional, Default=""_
```toml tab="File (TOML)"
[providers.consulCatalog]
constraints = "Label(`a.label.name`, `foo`)"
constraints = "Tag(`a.tag.name`)"
# ...
```
```yaml tab="File (YAML)"
providers:
consulCatalog:
constraints: "Label(`a.label.name`, `foo`)"
constraints: "Tag(`a.tag.name`)"
# ...
```
```bash tab="CLI"
--providers.consulcatalog.constraints="Label(`a.label.name`, `foo`)"
--providers.consulcatalog.constraints="Tag(`a.tag.name`)"
# ...
```
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
That is to say, if none of the container's labels match the expression, no route for the container is created.
If the expression is empty, all detected containers are included.
Constraints is an expression that Traefik matches against the service's tags to determine whether to create any route for that service.
That is to say, if none of the service's tags match the expression, no route for that service is created.
If the expression is empty, all detected services are included.
The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
The expression syntax is based on the `Tag("tag")`, and `TagRegex("tag")` functions,
as well as the usual boolean logic, as shown in examples below.
??? example "Constraints Expression Examples"
```toml
# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
# Includes only services having the tag `a.tag.name=foo`
constraints = "Tag(`a.tag.name=foo`)"
```
```toml
# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
# Excludes services having any tag `a.tag.name=foo`
constraints = "!Tag(`a.tag.name=foo`)"
```
```toml
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
constraints = "Tag(`a.tag.name`) && Tag(`another.tag.name`)"
```
```toml
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
constraints = "Tag(`a.tag.name`) || Tag(`another.tag.name`)"
```
```toml
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
constraints = "Tag(`a.tag.name`) && (Tag(`another.tag.name`) || Tag(`yet.another.tag.name`))"
```
```toml
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegex(`a.label.name`, `a.+`)"
# Includes only services having a tag matching the `a\.tag\.t.+` regular expression.
constraints = "TagRegex(`a\.tag\.t.+`)"
```
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).

View file

@ -1,37 +1,37 @@
# Traefik & Consul Catalog
A Story of Labels, Services & Containers
A Story of Tags, Services & Instances
{: .subtitle }
![Rancher](../../assets/img/providers/consul.png)
Attach labels to your services and let Traefik do the rest!
Attach tags to your services and let Traefik do the rest!
## Routing Configuration
!!! info "Labels"
!!! info "tags"
- Labels are case insensitive.
- The complete list of labels can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md)
- tags are case insensitive.
- The complete list of tags can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md)
### General
Traefik creates, for each consul Catalog service, a corresponding [service](../services/index.md) and [router](../routers/index.md).
The Service automatically gets a server per container in this consul Catalog service, and the router gets a default rule attached to it, based on the service name.
The Service automatically gets a server per instance in this consul Catalog service, and the router gets a default rule attached to it, based on the service name.
### Routers
To update the configuration of the Router automatically attached to the container, add labels starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change.
To update the configuration of the Router automatically attached to the service, add tags starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change.
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
For example, to change the rule, you could add the tag ```traefik.http.routers.my-service.rule=Host(`mydomain.com`)```.
??? info "`traefik.http.routers.<router_name>.rule`"
See [rule](../routers/index.md#rule) for more information.
```yaml
- "traefik.http.routers.myrouter.rule=Host(`mydomain.com`)"
traefik.http.routers.myrouter.rule=Host(`mydomain.com`)
```
??? info "`traefik.http.routers.<router_name>.entrypoints`"
@ -39,7 +39,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [entry points](../routers/index.md#entrypoints) for more information.
```yaml
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
traefik.http.routers.myrouter.entrypoints=web,websecure
```
??? info "`traefik.http.routers.<router_name>.middlewares`"
@ -47,7 +47,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
```yaml
- "traefik.http.routers.myrouter.middlewares=auth,prefix,cb"
traefik.http.routers.myrouter.middlewares=auth,prefix,cb
```
??? info "`traefik.http.routers.<router_name>.service`"
@ -55,7 +55,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [rule](../routers/index.md#service) for more information.
```yaml
- "traefik.http.routers.myrouter.service=myservice"
traefik.http.routers.myrouter.service=myservice
```
??? info "`traefik.http.routers.<router_name>.tls`"
@ -63,7 +63,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [tls](../routers/index.md#tls) for more information.
```yaml
- "traefik.http.routers.myrouter>.tls=true"
traefik.http.routers.myrouter>.tls=true
```
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
@ -71,7 +71,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [certResolver](../routers/index.md#certresolver) for more information.
```yaml
- "traefik.http.routers.myrouter.tls.certresolver=myresolver"
traefik.http.routers.myrouter.tls.certresolver=myresolver
```
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
@ -79,7 +79,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [domains](../routers/index.md#domains) for more information.
```yaml
- "traefik.http.routers.myrouter.tls.domains[0].main=foobar.com"
traefik.http.routers.myrouter.tls.domains[0].main=foobar.com
```
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
@ -87,7 +87,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [domains](../routers/index.md#domains) for more information.
```yaml
- "traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com
```
??? info "`traefik.http.routers.<router_name>.tls.options`"
@ -95,31 +95,31 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [options](../routers/index.md#options) for more information.
```yaml
- "traefik.http.routers.myrouter.tls.options=foobar"
traefik.http.routers.myrouter.tls.options=foobar
```
??? info "`traefik.http.routers.<router_name>.priority`"
<!-- TODO doc priority in routers page -->
```yaml
- "traefik.http.routers.myrouter.priority=42"
traefik.http.routers.myrouter.priority=42
```
### Services
To update the configuration of the Service automatically attached to the container,
add labels starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change.
To update the configuration of the Service automatically attached to the service,
add tags starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change.
For example, to change the `passHostHeader` behavior,
you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
Registers a port.
Useful when the container exposes multiples ports.
Useful when the service exposes multiples ports.
```yaml
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
traefik.http.services.myservice.loadbalancer.server.port=8080
```
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
@ -127,14 +127,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
Overrides the default scheme.
```yaml
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"
traefik.http.services.myservice.loadbalancer.server.scheme=http
```
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
<!-- TODO doc passHostHeader in services page -->
```yaml
- "traefik.http.services.myservice.loadbalancer.passhostheader=true"
traefik.http.services.myservice.loadbalancer.passhostheader=true
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
@ -142,7 +142,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar"
traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
@ -150,7 +150,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com"
traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
@ -158,7 +158,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10"
traefik.http.services.myservice.loadbalancer.healthcheck.interval=10
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
@ -166,7 +166,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo"
traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
@ -174,7 +174,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.port=42"
traefik.http.services.myservice.loadbalancer.healthcheck.port=42
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
@ -182,7 +182,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http"
traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http
```
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
@ -190,7 +190,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [health check](../services/index.md#health-check) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
@ -198,7 +198,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.sticky=true"
traefik.http.services.myservice.loadbalancer.sticky=true
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
@ -206,7 +206,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true"
traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
@ -214,7 +214,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar"
traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
@ -222,7 +222,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true
```
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
@ -231,12 +231,12 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
FlushInterval specifies the flush interval to flush to the client while copying the response body.
```yaml
- "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10"
traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10
```
### Middleware
You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
You can declare pieces of middleware using tags starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
For example, to declare a middleware [`redirectscheme`](../../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`.
@ -246,11 +246,10 @@ More information about available middlewares in the dedicated [middlewares secti
```yaml
# ...
labels:
# Declaring a middleware
- traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
# Referencing a middleware
- traefik.http.routers.my-container.middlewares=my-redirect
traefik.http.routers.my-service.middlewares=my-redirect
```
!!! warning "Conflicts in Declaration"
@ -259,24 +258,20 @@ More information about available middlewares in the dedicated [middlewares secti
### TCP
You can declare TCP Routers and/or Services using labels.
You can declare TCP Routers and/or Services using tags.
??? example "Declaring TCP Routers and Services"
```yaml
services:
my-container:
# ...
labels:
- "traefik.tcp.routers.my-router.rule=HostSNI(`my-host.com`)"
- "traefik.tcp.routers.my-router.tls=true"
- "traefik.tcp.services.my-service.loadbalancer.server.port=4123"
traefik.tcp.routers.my-router.rule=HostSNI(`my-host.com`)
traefik.tcp.routers.my-router.tls=true
traefik.tcp.services.my-service.loadbalancer.server.port=4123
```
!!! warning "TCP and HTTP"
If you declare a TCP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no TCP Router/Service is defined).
You can declare both a TCP Router/Service and an HTTP Router/Service for the same container (but you have to do so manually).
You can declare both a TCP Router/Service and an HTTP Router/Service for the same consul service (but you have to do so manually).
#### TCP Routers
@ -285,7 +280,7 @@ You can declare TCP Routers and/or Services using labels.
See [entry points](../routers/index.md#entrypoints_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2"
traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2
```
??? info "`traefik.tcp.routers.<router_name>.rule`"
@ -293,7 +288,7 @@ You can declare TCP Routers and/or Services using labels.
See [rule](../routers/index.md#rule_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)"
traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)
```
??? info "`traefik.tcp.routers.<router_name>.service`"
@ -301,7 +296,7 @@ You can declare TCP Routers and/or Services using labels.
See [service](../routers/index.md#services) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.service=myservice"
traefik.tcp.routers.mytcprouter.service=myservice
```
??? info "`traefik.tcp.routers.<router_name>.tls`"
@ -309,7 +304,7 @@ You can declare TCP Routers and/or Services using labels.
See [TLS](../routers/index.md#tls_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls=true"
traefik.tcp.routers.mytcprouter.tls=true
```
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
@ -317,7 +312,7 @@ You can declare TCP Routers and/or Services using labels.
See [certResolver](../routers/index.md#certresolver_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver"
traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver
```
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
@ -325,7 +320,7 @@ You can declare TCP Routers and/or Services using labels.
See [domains](../routers/index.md#domains_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com"
traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com
```
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
@ -333,7 +328,7 @@ You can declare TCP Routers and/or Services using labels.
See [domains](../routers/index.md#domains_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com
```
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
@ -341,7 +336,7 @@ You can declare TCP Routers and/or Services using labels.
See [options](../routers/index.md#options_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls.options=mysoptions"
traefik.tcp.routers.mytcprouter.tls.options=mysoptions
```
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
@ -349,7 +344,7 @@ You can declare TCP Routers and/or Services using labels.
See [TLS](../routers/index.md#tls_1) for more information.
```yaml
- "traefik.tcp.routers.mytcprouter.tls.passthrough=true"
traefik.tcp.routers.mytcprouter.tls.passthrough=true
```
#### TCP Services
@ -359,7 +354,7 @@ You can declare TCP Routers and/or Services using labels.
Registers a port of the application.
```yaml
- "traefik.tcp.services.mytcpservice.loadbalancer.server.port=423"
traefik.tcp.services.mytcpservice.loadbalancer.server.port=423
```
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
@ -367,7 +362,7 @@ You can declare TCP Routers and/or Services using labels.
See [termination delay](../services/index.md#termination-delay) for more information.
```yaml
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
```
### Specific Provider Options
@ -375,10 +370,10 @@ You can declare TCP Routers and/or Services using labels.
#### `traefik.enable`
```yaml
- "traefik.enable=true"
traefik.enable=true
```
You can tell Traefik to consider (or not) the container by setting `traefik.enable` to true or false.
You can tell Traefik to consider (or not) the service by setting `traefik.enable` to true or false.
This option overrides the value of `exposedByDefault`.

View file

@ -12,23 +12,23 @@ import (
// It is used in order to create a specific and unique pattern for these labels.
const MarathonConstraintPrefix = "Traefik-Marathon-505F9E15-BDC7-45E7-828D-C06C7BAB8091"
type constraintFunc func(map[string]string) bool
type constraintLabelFunc func(map[string]string) bool
// Match reports whether the expression matches with the given labels.
// MatchLabels reports whether the expression matches with the given labels.
// The expression must match any logical boolean combination of:
// - `Label(labelName, labelValue)`
// - `LabelRegex(labelName, regexValue)`
// - `MarathonConstraint(field:operator:value)`
func Match(labels map[string]string, expr string) (bool, error) {
func MatchLabels(labels map[string]string, expr string) (bool, error) {
if expr == "" {
return true, nil
}
p, err := predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
AND: andFunc,
NOT: notFunc,
OR: orFunc,
AND: andLabelFunc,
NOT: notLabelFunc,
OR: orLabelFunc,
},
Functions: map[string]interface{}{
"Label": labelFn,
@ -45,20 +45,20 @@ func Match(labels map[string]string, expr string) (bool, error) {
return false, err
}
fn, ok := parse.(constraintFunc)
fn, ok := parse.(constraintLabelFunc)
if !ok {
return false, errors.New("not a constraintFunc")
return false, errors.New("not a constraintLabelFunc")
}
return fn(labels), nil
}
func labelFn(name, value string) constraintFunc {
func labelFn(name, value string) constraintLabelFunc {
return func(labels map[string]string) bool {
return labels[name] == value
}
}
func labelRegexFn(name, expr string) constraintFunc {
func labelRegexFn(name, expr string) constraintLabelFunc {
return func(labels map[string]string) bool {
matched, err := regexp.MatchString(expr, labels[name])
if err != nil {
@ -68,7 +68,7 @@ func labelRegexFn(name, expr string) constraintFunc {
}
}
func marathonFn(value string) constraintFunc {
func marathonFn(value string) constraintLabelFunc {
return func(labels map[string]string) bool {
for k, v := range labels {
if strings.HasPrefix(k, MarathonConstraintPrefix) {
@ -81,19 +81,19 @@ func marathonFn(value string) constraintFunc {
}
}
func andFunc(a, b constraintFunc) constraintFunc {
func andLabelFunc(a, b constraintLabelFunc) constraintLabelFunc {
return func(labels map[string]string) bool {
return a(labels) && b(labels)
}
}
func orFunc(a, b constraintFunc) constraintFunc {
func orLabelFunc(a, b constraintLabelFunc) constraintLabelFunc {
return func(labels map[string]string) bool {
return a(labels) || b(labels)
}
}
func notFunc(a constraintFunc) constraintFunc {
func notLabelFunc(a constraintLabelFunc) constraintLabelFunc {
return func(labels map[string]string) bool {
return !a(labels)
}

View file

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestMatch(t *testing.T) {
func TestMatchLabels(t *testing.T) {
testCases := []struct {
expr string
labels map[string]string
@ -192,7 +192,7 @@ func TestMatch(t *testing.T) {
t.Run(test.expr, func(t *testing.T) {
t.Parallel()
matches, err := Match(test.labels, test.expr)
matches, err := MatchLabels(test.labels, test.expr)
if test.expectedErr {
require.Error(t, err)
} else {

View file

@ -0,0 +1,92 @@
package constraints
import (
"errors"
"regexp"
"github.com/vulcand/predicate"
)
type constraintTagFunc func([]string) bool
// MatchTags reports whether the expression matches with the given tags.
// The expression must match any logical boolean combination of:
// - `Tag(tagValue)`
// - `TagRegex(regexValue)`
func MatchTags(tags []string, expr string) (bool, error) {
if expr == "" {
return true, nil
}
p, err := predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
AND: andTagFunc,
NOT: notTagFunc,
OR: orTagFunc,
},
Functions: map[string]interface{}{
"Tag": tagFn,
"TagRegex": tagRegexFn,
},
})
if err != nil {
return false, err
}
parse, err := p.Parse(expr)
if err != nil {
return false, err
}
fn, ok := parse.(constraintTagFunc)
if !ok {
return false, errors.New("not a constraintTagFunc")
}
return fn(tags), nil
}
func tagFn(name string) constraintTagFunc {
return func(tags []string) bool {
for _, tag := range tags {
if tag == name {
return true
}
}
return false
}
}
func tagRegexFn(expr string) constraintTagFunc {
return func(tags []string) bool {
exp, err := regexp.Compile(expr)
if err != nil {
return false
}
for _, tag := range tags {
if exp.MatchString(tag) {
return true
}
}
return false
}
}
func andTagFunc(a, b constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return a(tags) && b(tags)
}
}
func orTagFunc(a, b constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return a(tags) || b(tags)
}
}
func notTagFunc(a constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return !a(tags)
}
}

View file

@ -0,0 +1,111 @@
package constraints
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMatchTags(t *testing.T) {
testCases := []struct {
expr string
tags []string
expected bool
expectedErr bool
}{
{
expr: `Tag("world")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `Tag("worlds")`,
tags: []string{"hello", "world"},
expected: false,
},
{
expr: `!Tag("world")`,
tags: []string{"hello", "world"},
expected: false,
},
{
expr: `Tag("hello") && Tag("world")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `Tag("hello") && Tag("worlds")`,
tags: []string{"hello", "world"},
expected: false,
},
{
expr: `Tag("hello") && !Tag("world")`,
tags: []string{"hello", "world"},
expected: false,
},
{
expr: `Tag("hello") || Tag( "world")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `Tag( "worlds") || Tag("hello")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `Tag("hello") || !Tag("world")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `Tag()`,
tags: []string{"hello", "world"},
expectedErr: true,
},
{
expr: `Foo("hello")`,
tags: []string{"hello", "world"},
expectedErr: true,
},
{
expr: `Tag("hello")`,
expected: false,
},
{
expr: ``,
expected: true,
},
{
expr: `TagRegex("hel\\w+")`,
tags: []string{"hello", "world"},
expected: true,
},
{
expr: `TagRegex("hell\\w+s")`,
tags: []string{"hello", "world"},
expected: false,
},
{
expr: `!TagRegex("hel\\w+")`,
tags: []string{"hello", "world"},
expected: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.expr, func(t *testing.T) {
t.Parallel()
matches, err := MatchTags(test.tags, test.expr)
if test.expectedErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
assert.Equal(t, test.expected, matches)
})
}
}

View file

@ -80,7 +80,7 @@ func (p *Provider) keepContainer(ctx context.Context, item itemData) bool {
return false
}
matches, err := constraints.Match(item.Labels, p.Constraints)
matches, err := constraints.MatchTags(item.Tags, p.Constraints)
if err != nil {
logger.Errorf("Error matching constraints expression: %v", err)
return false

View file

@ -2,6 +2,7 @@ package consulcatalog
import (
"context"
"fmt"
"testing"
"github.com/containous/traefik/v2/pkg/config/dynamic"
@ -1541,7 +1542,7 @@ func Test_buildConfiguration(t *testing.T) {
Status: api.HealthPassing,
},
},
constraints: `Label("traefik.tags", "bar")`,
constraints: `Tag("traefik.tags=bar")`,
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
@ -1568,7 +1569,7 @@ func Test_buildConfiguration(t *testing.T) {
Status: api.HealthPassing,
},
},
constraints: `Label("traefik.tags", "foo")`,
constraints: `Tag("traefik.tags=foo")`,
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
@ -1955,6 +1956,12 @@ func Test_buildConfiguration(t *testing.T) {
var err error
test.items[i].ExtraConf, err = p.getConfiguration(test.items[i])
require.NoError(t, err)
var tags []string
for k, v := range test.items[i].Labels {
tags = append(tags, fmt.Sprintf("%s=%s", k, v))
}
test.items[i].Tags = tags
}
configuration := p.buildConfiguration(context.Background(), test.items)

View file

@ -30,6 +30,7 @@ type itemData struct {
Port string
Status string
Labels map[string]string
Tags []string
ExtraConf configuration
}
@ -157,7 +158,6 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
}
for _, consulService := range consulServices {
labels := tagsToNeutralLabels(consulService.ServiceTags, p.Prefix)
address := consulService.ServiceAddress
if address == "" {
address = consulService.Address
@ -169,7 +169,8 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
Name: consulService.ServiceName,
Address: address,
Port: strconv.Itoa(consulService.ServicePort),
Labels: labels,
Labels: tagsToNeutralLabels(consulService.ServiceTags, p.Prefix),
Tags: consulService.ServiceTags,
Status: consulService.Checks.AggregatedStatus(),
}

View file

@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestTagsToNeutralLabels(t *testing.T) {
func Test_tagsToNeutralLabels(t *testing.T) {
testCases := []struct {
desc string
tags []string

View file

@ -127,7 +127,7 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
return false
}
matches, err := constraints.Match(container.Labels, p.Constraints)
matches, err := constraints.MatchLabels(container.Labels, p.Constraints)
if err != nil {
logger.Errorf("Error matching constraints expression: %v", err)
return false

View file

@ -185,7 +185,7 @@ func (p *Provider) keepApplication(ctx context.Context, extraConf configuration,
}
// Filter by constraints.
matches, err := constraints.Match(labels, p.Constraints)
matches, err := constraints.MatchLabels(labels, p.Constraints)
if err != nil {
logger.Errorf("Error matching constraints expression: %v", err)
return false

View file

@ -121,7 +121,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
return false
}
matches, err := constraints.Match(service.Labels, p.Constraints)
matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
if err != nil {
logger.Errorf("Error matching constraints expression: %v", err)
return false