diff --git a/docs/content/providers/consul-catalog.md b/docs/content/providers/consul-catalog.md index c5fb286b0..0b549ae47 100644 --- a/docs/content/providers/consul-catalog.md +++ b/docs/content/providers/consul-catalog.md @@ -1,17 +1,17 @@ # 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 ??? example "Configuring Consul Catalog & Deploying / Exposing Services" - Enabling the consulcatalog provider + Enabling the consul catalog provider ```toml tab="File (TOML)" [providers.consulCatalog] @@ -26,11 +26,10 @@ 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`) + - traefik.http.services.my-service.rule=Host(`mydomain.com`) ``` ## Routing Configuration @@ -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). diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index bdbbe44cb..22b4978d6 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -1,61 +1,61 @@ # 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..rule`" - See [rule](../routers/index.md#rule) for more information. + 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..entrypoints`" - See [entry points](../routers/index.md#entrypoints) for more information. + 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..middlewares`" - See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information. + 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..service`" - See [rule](../routers/index.md#service) for more information. + 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..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..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..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..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..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..priority`" ```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..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..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..loadbalancer.passhostheader`" ```yaml - - "traefik.http.services.myservice.loadbalancer.passhostheader=true" + traefik.http.services.myservice.loadbalancer.passhostheader=true ``` ??? info "`traefik.http.services..loadbalancer.healthcheck.headers.`" @@ -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..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..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..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..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..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..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..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..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..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..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..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 - # Referencing a middleware - - traefik.http.routers.my-container.middlewares=my-redirect + # Declaring a middleware + traefik.http.middlewares.my-redirect.redirectscheme.scheme=https + # Referencing a middleware + 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..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..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..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..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..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..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..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..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..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`. diff --git a/pkg/provider/constraints/constraints.go b/pkg/provider/constraints/constraints_labels.go similarity index 70% rename from pkg/provider/constraints/constraints.go rename to pkg/provider/constraints/constraints_labels.go index 5094442b3..cbaa5086b 100644 --- a/pkg/provider/constraints/constraints.go +++ b/pkg/provider/constraints/constraints_labels.go @@ -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) } diff --git a/pkg/provider/constraints/constraints_test.go b/pkg/provider/constraints/constraints_labels_test.go similarity index 97% rename from pkg/provider/constraints/constraints_test.go rename to pkg/provider/constraints/constraints_labels_test.go index c6198a329..43fb9e0c6 100644 --- a/pkg/provider/constraints/constraints_test.go +++ b/pkg/provider/constraints/constraints_labels_test.go @@ -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 { diff --git a/pkg/provider/constraints/constraints_tags.go b/pkg/provider/constraints/constraints_tags.go new file mode 100644 index 000000000..2a2fa3ffa --- /dev/null +++ b/pkg/provider/constraints/constraints_tags.go @@ -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) + } +} diff --git a/pkg/provider/constraints/constraints_tags_test.go b/pkg/provider/constraints/constraints_tags_test.go new file mode 100644 index 000000000..a563b0b30 --- /dev/null +++ b/pkg/provider/constraints/constraints_tags_test.go @@ -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) + }) + } +} diff --git a/pkg/provider/consulcatalog/config.go b/pkg/provider/consulcatalog/config.go index 0d2a476fb..1d0407eea 100644 --- a/pkg/provider/consulcatalog/config.go +++ b/pkg/provider/consulcatalog/config.go @@ -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 diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 14bb347e1..d4259aa58 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -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) diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index a176ab0b4..4c52eac16 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -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(), } diff --git a/pkg/provider/consulcatalog/convert_types_test.go b/pkg/provider/consulcatalog/convert_types_test.go index b0c24b02d..8dece14c2 100644 --- a/pkg/provider/consulcatalog/convert_types_test.go +++ b/pkg/provider/consulcatalog/convert_types_test.go @@ -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 diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index b2385a7e6..5753053dc 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -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 diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go index e2f81c163..9e76640e6 100644 --- a/pkg/provider/marathon/config.go +++ b/pkg/provider/marathon/config.go @@ -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 diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go index 6efc00173..c37cef01c 100644 --- a/pkg/provider/rancher/config.go +++ b/pkg/provider/rancher/config.go @@ -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