New constraints management.
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
parent
e9792b446f
commit
fe68e9e243
40 changed files with 658 additions and 630 deletions
15
Gopkg.lock
generated
15
Gopkg.lock
generated
|
@ -1415,14 +1415,6 @@
|
|||
pruneopts = "NUT"
|
||||
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:09d61699d553a4e6ec998ad29816177b1f3d3ed0c18fe923d2c174ec065c99c8"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "256dc444b735e061061cf46c809487313d5b0065"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a"
|
||||
name = "github.com/sacloud/libsacloud"
|
||||
|
@ -1598,12 +1590,12 @@
|
|||
revision = "3d629cff40b7040e0519628e7774ed11a95d9aff"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ca6bac407fedc14fbeeba861dd33a821ba3a1624c10126ec6003b0a28d4139c5"
|
||||
digest = "1:b9d8cc221fb40078c7eb78d73b1702b5b548511b3d62bbd56b2f8180089c79af"
|
||||
name = "github.com/vulcand/predicate"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "939c094524d124c55fa8afe0e077701db4a865e2"
|
||||
version = "v1.0.0"
|
||||
revision = "8fbfb3ab0e94276b6b58bec378600829adc7a203"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -2289,7 +2281,6 @@
|
|||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"github.com/prometheus/client_model/go",
|
||||
"github.com/rancher/go-rancher-metadata/metadata",
|
||||
"github.com/ryanuber/go-glob",
|
||||
"github.com/sirupsen/logrus",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
|
|
|
@ -146,10 +146,6 @@ required = [
|
|||
name = "github.com/rancher/go-rancher-metadata"
|
||||
source = "github.com/containous/go-rancher-metadata"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Masterminds/sprig"
|
||||
version = "2.19.0"
|
||||
|
|
|
@ -10,10 +10,10 @@ Configuration in Traefik can refer to two different things:
|
|||
- The fully dynamic routing configuration (referred to as the _dynamic configuration_)
|
||||
- The startup configuration (referred to as the _static configuration_)
|
||||
|
||||
Elements in the _static configuration_ set up connections to [providers](../../providers/overview/) and define the [entrypoints](../../routing/entrypoints/) Traefik will listen to (these elements don't change often).
|
||||
Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
|
||||
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interuption or connection loss.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
||||
|
||||
!!! warning "Incompatible Configuration"
|
||||
Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now.
|
||||
|
|
|
@ -26,7 +26,7 @@ traefik [--flag=flag_argument] [-f [flag_argument]]
|
|||
traefik [--flag[=true|false| ]] [-f [true|false| ]]
|
||||
```
|
||||
|
||||
### healthcheck
|
||||
### `healthcheck`
|
||||
|
||||
Calls Traefik `/ping` to check the health of Traefik.
|
||||
Its exit status is `0` if Traefik is healthy and `1` otherwise.
|
||||
|
@ -50,12 +50,12 @@ $ traefik healthcheck
|
|||
OK: http://:8082/ping
|
||||
```
|
||||
|
||||
### version
|
||||
### `version`
|
||||
|
||||
Shows the current Traefik version.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
traefik version [command] [flags] [arguments]
|
||||
traefik version
|
||||
```
|
||||
|
|
|
@ -37,7 +37,7 @@ Attach labels to your containers and let Traefik do the rest!
|
|||
Enabling the docker provider (Swarm Mode)
|
||||
|
||||
```toml
|
||||
[docker]
|
||||
[providers.docker]
|
||||
# swarm classic (1.12-)
|
||||
# endpoint = "tcp://127.0.0.1:2375"
|
||||
# docker swarm mode (1.12+)
|
||||
|
@ -193,8 +193,8 @@ The container service name can be accessed as the `Name` identifier,
|
|||
and the template has access to all the labels defined on this container.
|
||||
|
||||
```toml tab="File"
|
||||
[docker]
|
||||
defaultRule = ""
|
||||
[providers.docker]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -215,6 +215,48 @@ _Optional, Default=15_
|
|||
|
||||
Defines the polling interval (in seconds) in Swarm Mode.
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
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.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` 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`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```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`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
## Routing Configuration Options
|
||||
|
||||
### General
|
||||
|
@ -286,10 +328,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
|
|||
|
||||
This option overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### `traefik.docker.network`
|
||||
|
||||
Overrides the default docker network to use for connections to the container.
|
||||
|
|
|
@ -78,7 +78,7 @@ DCOSToken for DCOS environment.
|
|||
If set, it overrides the Authorization header.
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
[providers.marathon]
|
||||
dcosToken = "xxxxxx"
|
||||
# ...
|
||||
```
|
||||
|
@ -101,8 +101,8 @@ The app ID can be accessed as the Name identifier,
|
|||
and the template has access to all the labels defined on this Marathon application.
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
defaultRule = ""
|
||||
[providers.marathon]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -132,7 +132,7 @@ Marathon server endpoint.
|
|||
You can optionally specify multiple endpoints:
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
[providers.marathon]
|
||||
endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
||||
# ...
|
||||
```
|
||||
|
@ -150,16 +150,59 @@ Exposes Marathon applications by default through Traefik.
|
|||
|
||||
If set to false, applications that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
|
||||
|
||||
### `filterMarathonConstraints`
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=""_
|
||||
|
||||
Enables filtering using Marathon constraints.
|
||||
Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
|
||||
That is to say, if none of the application's labels match the expression, no route for the application is created.
|
||||
In addition, the expression also matched against the application's constraints, such as described in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html).
|
||||
If the expression is empty, all detected applications are included.
|
||||
|
||||
If enabled, Traefik will take into account Marathon constraints, as defined in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html).
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")`, as well as the usual boolean logic.
|
||||
In addition, to match against marathon constraints, the function `MarathonConstraint("field:operator:value")` can be used, where the field, operator, and value parts are joined together in a single string with the `:` separator.
|
||||
|
||||
Each individual constraint will be treated as a verbatim compounded tag,
|
||||
e.g. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":".
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only applications having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes applications having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```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`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only applications having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only applications having a Marathon constraint with field `A`, operator `B`, and value `C`.
|
||||
constraints = "MarathonConstraint(`A:B:C`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Uses both Marathon constraint and application label with logical operator.
|
||||
constraints = "MarathonConstraint(`A:B:C`) && Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
### `forceTaskHostname`
|
||||
|
||||
|
@ -318,10 +361,6 @@ You can declare TCP Routers and/or Services using labels.
|
|||
Setting this option controls whether Traefik exposes the application.
|
||||
It overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### `traefik.marathon.ipadressidx`
|
||||
|
||||
If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select.
|
||||
|
|
|
@ -27,73 +27,45 @@ Even if each provider is different, we can categorize them in four groups:
|
|||
Below is the list of the currently supported providers in Traefik.
|
||||
|
||||
| Provider | Type | Configuration Type |
|
||||
|---------------------------------|--------------|--------------------|
|
||||
|-----------------------------------|--------------|--------------------|
|
||||
| [Docker](./docker.md) | Orchestrator | Label |
|
||||
| [File](./file.md) | Orchestrator | Custom Annotation |
|
||||
| [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource |
|
||||
| [Marathon](marathon.md) | Orchestrator | Label |
|
||||
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource |
|
||||
| [Marathon](./marathon.md) | Orchestrator | Label |
|
||||
| [Rancher](./rancher.md) | Orchestrator | Label |
|
||||
| [File](./file.md) | Manual | TOML format |
|
||||
|
||||
!!! note "More Providers"
|
||||
|
||||
The current version of Traefik is in development and doesn't support (yet) every provider. See the previous version (1.7) for more providers.
|
||||
The current version of Traefik is in development and doesn't support (yet) every provider.
|
||||
See the previous version (1.7) for more providers.
|
||||
|
||||
<!--
|
||||
TODO (document TCP VS HTTP dynamic configuration)
|
||||
-->
|
||||
TODO (document TCP VS HTTP dynamic configuration)
|
||||
-->
|
||||
|
||||
## Constraints Configuration
|
||||
## Restrict the Scope of Service Discovery
|
||||
|
||||
If you want to limit the scope of Traefik's service discovery, you can set constraints.
|
||||
Doing so, Traefik will create routes for containers that match these constraints only.
|
||||
By default Traefik will create routes for all detected containers.
|
||||
|
||||
??? example "Containers with the api Tag"
|
||||
If you want to limit the scope of Traefik's service discovery,
|
||||
i.e. disallow route creation for some containers,
|
||||
you can do so in two different ways:
|
||||
either with the generic configuration option `exposedByDefault`,
|
||||
or with a finer granularity mechanism based on constraints.
|
||||
|
||||
```toml
|
||||
constraints = ["tag==api"]
|
||||
```
|
||||
### `exposedByDefault` and `traefik.enable`
|
||||
|
||||
??? example "Containers without the api Tag"
|
||||
List of providers that support that feature:
|
||||
|
||||
```toml
|
||||
constraints = ["tag!=api"]
|
||||
```
|
||||
- [Docker](./docker.md#exposedbydefault)
|
||||
- [Rancher](./rancher.md#exposedbydefault)
|
||||
- [Marathon](./marathon.md#exposedbydefault)
|
||||
|
||||
??? example "Containers with tags starting with 'us-'"
|
||||
### Constraints
|
||||
|
||||
```toml
|
||||
constraints = ["tag==us-*"]
|
||||
```
|
||||
List of providers that support constraints:
|
||||
|
||||
??? example "Multiple constraints"
|
||||
|
||||
```toml
|
||||
# Multiple constraints
|
||||
# - "tag==" must match with at least one tag
|
||||
# - "tag!=" must match with none of tags
|
||||
constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||
```
|
||||
|
||||
??? note "List of Providers that Support Constraints"
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
- BoltDB
|
||||
- Zookeeper
|
||||
- ECS
|
||||
- Etcd
|
||||
- Consul Catalog
|
||||
- Rancher
|
||||
- Marathon
|
||||
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||
|
||||
!!! note
|
||||
|
||||
The constraint option belongs to the provider configuration itself.
|
||||
|
||||
??? example "Setting the Constraint Options for Docker"
|
||||
|
||||
```toml
|
||||
[providers]
|
||||
[providers.docker]
|
||||
constraints = ["tag==api"]
|
||||
```
|
||||
- [Docker](./docker.md#constraints)
|
||||
- [Rancher](./rancher.md#constraints)
|
||||
- [Marathon](./marathon.md#constraints)
|
||||
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)
|
||||
|
|
|
@ -19,64 +19,23 @@ Attach labels to your services and let Traefik do the rest!
|
|||
Enabling the rancher provider
|
||||
|
||||
```toml
|
||||
[provider.rancher]
|
||||
[Providers.Rancher]
|
||||
```
|
||||
|
||||
Attaching labels to services
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- traefik.http.services.my-service.rule=Host(my-domain)
|
||||
- traefik.http.services.my-service.rule=Host(`my-domain`)
|
||||
```
|
||||
|
||||
## Provider Configuration Options
|
||||
|
||||
!!! tip "Browse the Reference"
|
||||
??? tip "Browse the Reference"
|
||||
If you're in a hurry, maybe you'd rather go through the configuration reference:
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher Provider
|
||||
################################################################
|
||||
|
||||
# Enable Rancher Provider.
|
||||
[rancher]
|
||||
|
||||
# Expose Rancher services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
ExposedByDefault = "true"
|
||||
|
||||
# Enable watch Rancher changes.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Filter services with unhealthy states and inactive states.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
EnableServiceHealthFilter = true
|
||||
|
||||
# Defines the polling interval (in seconds).
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
RefreshSeconds = true
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
IntervalPoll = false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
Prefix = 15
|
||||
--8<-- "content/providers/rancher.toml"
|
||||
```
|
||||
|
||||
### `ExposedByDefault`
|
||||
|
@ -99,8 +58,8 @@ The service name can be accessed as the `Name` identifier,
|
|||
and the template has access to all the labels defined on this container.
|
||||
|
||||
```toml tab="File"
|
||||
[rancher]
|
||||
defaultRule = ""
|
||||
[Providers.Rancher]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -136,6 +95,50 @@ _Optional, Default=/latest_
|
|||
|
||||
Prefix used for accessing the Rancher metadata service
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
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.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` 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`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```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`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
## Routing Configuration Options
|
||||
|
||||
### General
|
||||
|
||||
Traefik creates, for each rancher service, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md).
|
||||
|
@ -185,10 +188,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
|
|||
|
||||
This option overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### Port Lookup
|
||||
|
||||
Traefik is now capable of detecting the port to use, by following the default rancher flow.
|
||||
|
|
20
docs/content/providers/rancher.toml
Normal file
20
docs/content/providers/rancher.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Enable Rancher Provider.
|
||||
[Providers.Rancher]
|
||||
|
||||
# Expose Rancher services by default in Traefik.
|
||||
ExposedByDefault = true
|
||||
|
||||
# Enable watch Rancher changes.
|
||||
Watch = true
|
||||
|
||||
# Filter services with unhealthy states and inactive states.
|
||||
EnableServiceHealthFilter = true
|
||||
|
||||
# Defines the polling interval (in seconds).
|
||||
RefreshSeconds = true
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
|
||||
IntervalPoll = false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
Prefix = "/latest"
|
|
@ -249,17 +249,8 @@
|
|||
Enable Docker backend with default settings.
|
||||
|
||||
--providers.docker.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.docker.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.docker.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.docker.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels
|
||||
to determine whether to create any route for that container.
|
||||
|
||||
--providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||
Default rule.
|
||||
|
@ -382,17 +373,8 @@
|
|||
Basic authentication Password.
|
||||
|
||||
--providers.marathon.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.marathon.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.marathon.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.marathon.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the application's
|
||||
labels to determine whether to create any route for that application.
|
||||
|
||||
--providers.marathon.dcostoken (Default: "")
|
||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
||||
|
@ -409,9 +391,6 @@
|
|||
--providers.marathon.exposedbydefault (Default: "true")
|
||||
Expose Marathon apps by default.
|
||||
|
||||
--providers.marathon.filtermarathonconstraints (Default: "false")
|
||||
Enable use of Marathon constraints in constraint filtering.
|
||||
|
||||
--providers.marathon.forcetaskhostname (Default: "false")
|
||||
Force to use the task's hostname.
|
||||
|
||||
|
@ -457,17 +436,8 @@
|
|||
Enable Rancher backend with default settings.
|
||||
|
||||
--providers.rancher.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.rancher.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.rancher.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.rancher.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels
|
||||
to determine whether to create any route for that container.
|
||||
|
||||
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||
Default rule.
|
||||
|
|
|
@ -241,16 +241,7 @@ Middleware list.
|
|||
Enable Docker backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
@ -373,16 +364,7 @@ Basic authentication User.
|
|||
Basic authentication Password.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
|
||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
||||
|
@ -399,9 +381,6 @@ Marathon server endpoint. You can also specify multiple endpoint for Marathon. (
|
|||
`TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`:
|
||||
Expose Marathon apps by default. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_FILTERMARATHONCONSTRAINTS`:
|
||||
Enable use of Marathon constraints in constraint filtering. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`:
|
||||
Force to use the task's hostname. (Default: ```false```)
|
||||
|
||||
|
@ -445,16 +424,7 @@ Backends throttle duration: minimum duration between 2 events from providers bef
|
|||
Enable Rancher backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
|
|
@ -41,16 +41,7 @@
|
|||
SwarmMode = true
|
||||
Network = "foobar"
|
||||
SwarmModeRefreshSeconds = 42
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[Providers.Docker.TLS]
|
||||
CA = "foobar"
|
||||
|
@ -73,23 +64,13 @@
|
|||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
DCOSToken = "foobar"
|
||||
FilterMarathonConstraints = true
|
||||
DialerTimeout = 42
|
||||
ResponseHeaderTimeout = 42
|
||||
TLSHandshakeTimeout = 42
|
||||
KeepAlive = 42
|
||||
ForceTaskHostname = true
|
||||
RespectReadinessChecks = true
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[Providers.Marathon.TLS]
|
||||
CA = "foobar"
|
||||
|
@ -134,16 +115,7 @@
|
|||
RefreshSeconds = 42
|
||||
IntervalPoll = true
|
||||
Prefix = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[API]
|
||||
EntryPoint = "foobar"
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/ping"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/acme"
|
||||
acmeprovider "github.com/containous/traefik/pkg/provider/acme"
|
||||
"github.com/containous/traefik/pkg/provider/docker"
|
||||
|
@ -153,20 +152,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
config.Providers.Docker = &docker.Provider{
|
||||
Constrainer: provider.Constrainer{
|
||||
Constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "file Constraints Key 1",
|
||||
Value: "file Constraints Regex 2",
|
||||
MustMatch: true,
|
||||
},
|
||||
{
|
||||
Key: "file Constraints Key 1",
|
||||
Value: "file Constraints Regex 2",
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Constraints: `Label("foo", "bar")`,
|
||||
Watch: true,
|
||||
Endpoint: "MyEndPoint",
|
||||
DefaultRule: "PathPrefix(`/`)",
|
||||
|
|
|
@ -248,7 +248,6 @@ func Test_decodeFileToNode_Toml(t *testing.T) {
|
|||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
||||
{Name: "ForceTaskHostname", Value: "true"},
|
||||
{Name: "KeepAlive", Value: "42"},
|
||||
{Name: "RespectReadinessChecks", Value: "true"},
|
||||
|
@ -518,7 +517,6 @@ func Test_decodeFileToNode_Yaml(t *testing.T) {
|
|||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
||||
{Name: "ForceTaskHostname", Value: "true"},
|
||||
{Name: "KeepAlive", Value: "42"},
|
||||
{Name: "RespectReadinessChecks", Value: "true"},
|
||||
|
|
|
@ -74,7 +74,6 @@
|
|||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
DCOSToken = "foobar"
|
||||
FilterMarathonConstraints = true
|
||||
DialerTimeout = 42
|
||||
ResponseHeaderTimeout = 42
|
||||
TLSHandshakeTimeout = 42
|
||||
|
|
|
@ -69,7 +69,6 @@ Providers:
|
|||
DefaultRule: foobar
|
||||
ExposedByDefault: true
|
||||
DCOSToken: foobar
|
||||
FilterMarathonConstraints: true
|
||||
DialerTimeout: 42
|
||||
ResponseHeaderTimeout: 42
|
||||
TLSHandshakeTimeout: 42
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package provider
|
||||
|
||||
import "github.com/containous/traefik/pkg/types"
|
||||
|
||||
// Constrainer Filter services by constraint, matching with Traefik tags.
|
||||
type Constrainer struct {
|
||||
Constraints []*types.Constraint `description:"Filter services by constraint, matching with Traefik tags." export:"true"`
|
||||
}
|
||||
|
||||
// MatchConstraints must match with EVERY single constraint
|
||||
// returns first constraint that do not match or nil.
|
||||
func (c *Constrainer) MatchConstraints(tags []string) (bool, *types.Constraint) {
|
||||
// if there is no tags and no constraints, filtering is disabled
|
||||
if len(tags) == 0 && len(c.Constraints) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, constraint := range c.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
|
||||
}
|
||||
}
|
||||
|
||||
// If no constraint or every constraints matching
|
||||
return true, nil
|
||||
}
|
100
pkg/provider/constraints/constraints.go
Normal file
100
pkg/provider/constraints/constraints.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/vulcand/predicate"
|
||||
)
|
||||
|
||||
// MarathonConstraintPrefix is the prefix for each label's key created from a Marathon application constraint.
|
||||
// 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
|
||||
|
||||
// Match 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) {
|
||||
if expr == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
p, err := predicate.NewParser(predicate.Def{
|
||||
Operators: predicate.Operators{
|
||||
AND: andFunc,
|
||||
NOT: notFunc,
|
||||
OR: orFunc,
|
||||
},
|
||||
Functions: map[string]interface{}{
|
||||
"Label": labelFn,
|
||||
"LabelRegex": labelRegexFn,
|
||||
"MarathonConstraint": marathonFn,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
parse, err := p.Parse(expr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
fn, ok := parse.(constraintFunc)
|
||||
if !ok {
|
||||
return false, errors.New("not a constraintFunc")
|
||||
}
|
||||
return fn(labels), nil
|
||||
}
|
||||
|
||||
func labelFn(name, value string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return labels[name] == value
|
||||
}
|
||||
}
|
||||
|
||||
func labelRegexFn(name, expr string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
matched, err := regexp.MatchString(expr, labels[name])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return matched
|
||||
}
|
||||
}
|
||||
|
||||
func marathonFn(value string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
for k, v := range labels {
|
||||
if strings.HasPrefix(k, MarathonConstraintPrefix) {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func andFunc(a, b constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return a(labels) && b(labels)
|
||||
}
|
||||
}
|
||||
|
||||
func orFunc(a, b constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return a(labels) || b(labels)
|
||||
}
|
||||
}
|
||||
|
||||
func notFunc(a constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return !a(labels)
|
||||
}
|
||||
}
|
204
pkg/provider/constraints/constraints_test.go
Normal file
204
pkg/provider/constraints/constraints_test.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
expr string
|
||||
labels map[string]string
|
||||
expected bool
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
expr: `Label("hello", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hi", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `!Label("hello", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds") && Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && !Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") || Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds") || Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") || !Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
expr: `Foo("hello")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "bar")`,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: ``,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `MarathonConstraint("bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `MarathonConstraint("bur")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && MarathonConstraint("bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w\\w+s")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hi", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `!LabelRegex("hello", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w(\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.expr, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
matches, err := Match(test.labels, test.expr)
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, test.expected, matches)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/containous/traefik/pkg/config/label"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/constraints"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
|
@ -123,10 +124,13 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
|
|||
return false
|
||||
}
|
||||
|
||||
if ok, failingConstraint := p.MatchConstraints(container.ExtraConf.Tags); !ok {
|
||||
if failingConstraint != nil {
|
||||
logger.Debugf("Container pruned by %q constraint", failingConstraint.String())
|
||||
matches, err := constraints.Match(container.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Error("Error matching constraints expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
docker "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/go-connections/nat"
|
||||
|
@ -339,7 +338,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
containers []dockerData
|
||||
constraints []*types.Constraint
|
||||
constraints string
|
||||
expected *config.Configuration
|
||||
}{
|
||||
{
|
||||
|
@ -1924,13 +1923,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "bar")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1965,13 +1958,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "foo")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
|
|
@ -45,7 +45,7 @@ var _ provider.Provider = (*Provider)(nil)
|
|||
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
provider.Constrainer `description:"List of constraints used to filter out some containers." export:"true"`
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." export:"true"`
|
||||
Watch bool `description:"Watch provider." export:"true"`
|
||||
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."`
|
||||
DefaultRule string `description:"Default rule."`
|
||||
|
|
|
@ -14,7 +14,6 @@ const (
|
|||
// configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider.
|
||||
type configuration struct {
|
||||
Enable bool
|
||||
Tags []string
|
||||
Docker specificConfiguration
|
||||
}
|
||||
|
||||
|
@ -31,7 +30,7 @@ func (p *Provider) getConfiguration(container dockerData) (configuration, error)
|
|||
},
|
||||
}
|
||||
|
||||
err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable", "traefik.tags")
|
||||
err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable")
|
||||
if err != nil {
|
||||
return configuration{}, err
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/containous/traefik/pkg/config/label"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/constraints"
|
||||
"github.com/gambol99/go-marathon"
|
||||
)
|
||||
|
||||
|
@ -29,11 +30,20 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
|
|||
continue
|
||||
}
|
||||
|
||||
if !p.keepApplication(ctxApp, extraConf) {
|
||||
labels := stringValueMap(app.Labels)
|
||||
|
||||
if app.Constraints != nil {
|
||||
for i, constraintParts := range *app.Constraints {
|
||||
key := constraints.MarathonConstraintPrefix + "-" + strconv.Itoa(i)
|
||||
labels[key] = strings.Join(constraintParts, ":")
|
||||
}
|
||||
}
|
||||
|
||||
if !p.keepApplication(ctxApp, extraConf, labels) {
|
||||
continue
|
||||
}
|
||||
|
||||
confFromLabel, err := label.DecodeConfiguration(stringValueMap(app.Labels))
|
||||
confFromLabel, err := label.DecodeConfiguration(labels)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
continue
|
||||
|
@ -65,7 +75,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
|
|||
Labels map[string]string
|
||||
}{
|
||||
Name: app.ID,
|
||||
Labels: stringValueMap(app.Labels),
|
||||
Labels: labels,
|
||||
}
|
||||
|
||||
serviceName := getServiceName(app)
|
||||
|
@ -164,7 +174,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) keepApplication(ctx context.Context, extraConf configuration) bool {
|
||||
func (p *Provider) keepApplication(ctx context.Context, extraConf configuration, labels map[string]string) bool {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Filter disabled application.
|
||||
|
@ -174,10 +184,13 @@ func (p *Provider) keepApplication(ctx context.Context, extraConf configuration)
|
|||
}
|
||||
|
||||
// Filter by constraints.
|
||||
if ok, failingConstraint := p.MatchConstraints(extraConf.Tags); !ok {
|
||||
if failingConstraint != nil {
|
||||
logger.Debugf("Filtering Marathon application, pruned by %q constraint", failingConstraint.String())
|
||||
matches, err := constraints.Match(labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Error("Error matching constraints expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
logger.Debugf("Marathon application filtered by constraint expression: %q", p.Constraints)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -31,8 +30,7 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
applications *marathon.Applications
|
||||
constraints []*types.Constraint
|
||||
filterMarathonConstraints bool
|
||||
constraints string
|
||||
defaultRule string
|
||||
expected *config.Configuration
|
||||
}{
|
||||
|
@ -1065,13 +1063,7 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
withLabel("traefik.tags", "foo"),
|
||||
)),
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "bar")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1093,14 +1085,7 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
constraint("rack_id:CLUSTER:rack-1"),
|
||||
)),
|
||||
filterMarathonConstraints: true,
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "rack_id:CLUSTER:rack-2",
|
||||
},
|
||||
},
|
||||
constraints: `MarathonConstraint("rack_id:CLUSTER:rack-2")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1122,14 +1107,7 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
constraint("rack_id:CLUSTER:rack-1"),
|
||||
)),
|
||||
filterMarathonConstraints: true,
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "rack_id:CLUSTER:rack-1",
|
||||
},
|
||||
},
|
||||
constraints: `MarathonConstraint("rack_id:CLUSTER:rack-1")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1167,14 +1145,7 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
withLabel("traefik.tags", "bar"),
|
||||
)),
|
||||
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "bar")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1419,7 +1390,6 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
p := &Provider{
|
||||
DefaultRule: defaultRule,
|
||||
ExposedByDefault: true,
|
||||
FilterMarathonConstraints: test.filterMarathonConstraints,
|
||||
}
|
||||
p.Constraints = test.constraints
|
||||
|
||||
|
@ -1473,7 +1443,7 @@ func TestApplicationFilterEnabled(t *testing.T) {
|
|||
extraConf, err := provider.getConfiguration(app)
|
||||
require.NoError(t, err)
|
||||
|
||||
if provider.keepApplication(context.Background(), extraConf) != test.expected {
|
||||
if provider.keepApplication(context.Background(), extraConf, stringValueMap(app.Labels)) != test.expected {
|
||||
t.Errorf("got unexpected filtering = %t", !test.expected)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,7 +2,6 @@ package marathon
|
|||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/label"
|
||||
"github.com/gambol99/go-marathon"
|
||||
|
@ -10,7 +9,6 @@ import (
|
|||
|
||||
type configuration struct {
|
||||
Enable bool
|
||||
Tags []string
|
||||
Marathon specificConfiguration
|
||||
}
|
||||
|
||||
|
@ -23,23 +21,16 @@ func (p *Provider) getConfiguration(app marathon.Application) (configuration, er
|
|||
|
||||
conf := configuration{
|
||||
Enable: p.ExposedByDefault,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
}
|
||||
|
||||
err := label.Decode(labels, &conf, "traefik.marathon.", "traefik.enable", "traefik.tags")
|
||||
err := label.Decode(labels, &conf, "traefik.marathon.", "traefik.enable")
|
||||
if err != nil {
|
||||
return configuration{}, err
|
||||
}
|
||||
|
||||
if p.FilterMarathonConstraints && app.Constraints != nil {
|
||||
for _, constraintParts := range *app.Constraints {
|
||||
conf.Tags = append(conf.Tags, strings.Join(constraintParts, ":"))
|
||||
}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: false,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: false,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
|
@ -44,11 +42,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: false,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: true,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
|
@ -64,11 +60,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: false,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: false,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: 4,
|
||||
},
|
||||
|
@ -84,13 +78,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: false,
|
||||
FilterMarathonConstraints: true,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: false,
|
||||
Tags: []string{
|
||||
"key:value",
|
||||
},
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
|
@ -104,11 +94,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: true,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: true,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
|
@ -124,31 +112,9 @@ func TestGetConfiguration(t *testing.T) {
|
|||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: true,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: false,
|
||||
Tags: nil,
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Tags in label",
|
||||
app: marathon.Application{
|
||||
Constraints: &[][]string{},
|
||||
Labels: &map[string]string{
|
||||
"traefik.tags": "mytags",
|
||||
},
|
||||
},
|
||||
p: Provider{
|
||||
ExposedByDefault: true,
|
||||
FilterMarathonConstraints: false,
|
||||
},
|
||||
expected: configuration{
|
||||
Enable: true,
|
||||
Tags: []string{"mytags"},
|
||||
Marathon: specificConfiguration{
|
||||
IPAddressIdx: math.MinInt32,
|
||||
},
|
||||
|
|
|
@ -45,15 +45,13 @@ var _ provider.Provider = (*Provider)(nil)
|
|||
|
||||
// Provider holds configuration of the provider.
|
||||
type Provider struct {
|
||||
provider.Constrainer `description:"List of constraints used to filter out some containers." export:"true"`
|
||||
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application." export:"true"`
|
||||
Trace bool `description:"Display additional provider logs." export:"true"`
|
||||
Watch bool `description:"Watch provider." export:"true"`
|
||||
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon." export:"true"`
|
||||
DefaultRule string `description:"Default rule."`
|
||||
ExposedByDefault bool `description:"Expose Marathon apps by default." export:"true"`
|
||||
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." export:"true"`
|
||||
FilterMarathonConstraints bool `description:"Enable use of Marathon constraints in constraint filtering." export:"true"`
|
||||
TLS *types.ClientTLS `description:"Enable TLS support." export:"true"`
|
||||
DialerTimeout types.Duration `description:"Set a dialer timeout for Marathon." export:"true"`
|
||||
ResponseHeaderTimeout types.Duration `description:"Set a response header timeout for Marathon." export:"true"`
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/containous/traefik/pkg/config/label"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/constraints"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration(ctx context.Context, services []rancherData) *config.Configuration {
|
||||
|
@ -120,10 +121,13 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if ok, failingConstraint := p.MatchConstraints(service.ExtraConf.Tags); !ok {
|
||||
if failingConstraint != nil {
|
||||
logger.Debugf("service pruned by %q constraint", failingConstraint.String())
|
||||
matches, err := constraints.Match(service.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Error("Error matching constraints expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
logger.Debugf("Service pruned by constraint expression: %q", p.Constraints)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -14,7 +13,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
containers []rancherData
|
||||
constraints []*types.Constraint
|
||||
constraints string
|
||||
expected *config.Configuration
|
||||
}{
|
||||
{
|
||||
|
@ -330,13 +329,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
State: "",
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "bar")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -363,13 +356,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
State: "",
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "foo")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
type configuration struct {
|
||||
Enable bool
|
||||
Tags []string
|
||||
}
|
||||
|
||||
func (p *Provider) getConfiguration(service rancherData) (configuration, error) {
|
||||
|
@ -14,7 +13,7 @@ func (p *Provider) getConfiguration(service rancherData) (configuration, error)
|
|||
Enable: p.ExposedByDefault,
|
||||
}
|
||||
|
||||
err := label.Decode(service.Labels, &conf, "traefik.rancher.", "traefik.enable", "traefik.tags")
|
||||
err := label.Decode(service.Labels, &conf, "traefik.rancher.", "traefik.enable")
|
||||
if err != nil {
|
||||
return configuration{}, err
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ var _ provider.Provider = (*Provider)(nil)
|
|||
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
provider.Constrainer `description:"List of constraints used to filter out some containers." export:"true"`
|
||||
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." export:"true"`
|
||||
Watch bool `description:"Watch provider." export:"true"`
|
||||
DefaultRule string `description:"Default rule."`
|
||||
ExposedByDefault bool `description:"Expose containers by default." export:"true"`
|
||||
|
|
|
@ -25,7 +25,6 @@ type Provider struct {
|
|||
// SetDefaults sets the default values.
|
||||
func (p *Provider) SetDefaults() {
|
||||
p.EntryPoint = "traefik"
|
||||
// FIXME p.EntryPoint = static.DefaultInternalEntryPointName
|
||||
}
|
||||
|
||||
var templatesRenderer = render.New(render.Options{Directory: "nowhere"})
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ryanuber/go-glob"
|
||||
)
|
||||
|
||||
// Constraint holds a parsed constraint expression.
|
||||
// FIXME replace by a string.
|
||||
type Constraint struct {
|
||||
Key string `description:"The provider label that will be matched against. In practice, it is always 'tag'." export:"true"`
|
||||
// MustMatch is true if operator is "==" or false if operator is "!="
|
||||
MustMatch bool `description:"Whether the matching operator is equals or not equals." export:"true"`
|
||||
Value string `description:"The value that will be matched against." export:"true"` // TODO: support regex
|
||||
}
|
||||
|
||||
// NewConstraint receives a string and return a *Constraint, after checking syntax and parsing the constraint expression.
|
||||
func NewConstraint(exp string) (*Constraint, error) {
|
||||
sep := ""
|
||||
constraint := &Constraint{}
|
||||
|
||||
switch {
|
||||
case strings.Contains(exp, "=="):
|
||||
sep = "=="
|
||||
constraint.MustMatch = true
|
||||
case strings.Contains(exp, "!="):
|
||||
sep = "!="
|
||||
constraint.MustMatch = false
|
||||
default:
|
||||
return nil, errors.New("constraint expression missing valid operator: '==' or '!='")
|
||||
}
|
||||
|
||||
kv := strings.SplitN(exp, sep, 2)
|
||||
if len(kv) == 2 {
|
||||
// At the moment, it only supports tags
|
||||
if kv[0] != "tag" {
|
||||
return nil, errors.New("constraint must be tag-based. Syntax: tag==us-*")
|
||||
}
|
||||
|
||||
constraint.Key = kv[0]
|
||||
constraint.Value = kv[1]
|
||||
return constraint, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("incorrect constraint expression: %s", exp)
|
||||
}
|
||||
|
||||
func (c *Constraint) String() string {
|
||||
if c.MustMatch {
|
||||
return c.Key + "==" + c.Value
|
||||
}
|
||||
return c.Key + "!=" + c.Value
|
||||
}
|
||||
|
||||
var _ encoding.TextUnmarshaler = (*Constraint)(nil)
|
||||
|
||||
// UnmarshalText defines how unmarshal in TOML parsing
|
||||
func (c *Constraint) UnmarshalText(text []byte) error {
|
||||
constraint, err := NewConstraint(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Key = constraint.Key
|
||||
c.MustMatch = constraint.MustMatch
|
||||
c.Value = constraint.Value
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ encoding.TextMarshaler = (*Constraint)(nil)
|
||||
|
||||
// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
|
||||
func (c *Constraint) MarshalText() (text []byte, err error) {
|
||||
return []byte(c.String()), nil
|
||||
}
|
||||
|
||||
// MatchConstraintWithAtLeastOneTag tests a constraint for one single service.
|
||||
func (c *Constraint) MatchConstraintWithAtLeastOneTag(tags []string) bool {
|
||||
for _, tag := range tags {
|
||||
if glob.Glob(c.Value, tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -23,7 +23,6 @@ type Prometheus struct {
|
|||
func (p *Prometheus) SetDefaults() {
|
||||
p.Buckets = []float64{0.1, 0.3, 1.2, 5}
|
||||
p.EntryPoint = "traefik"
|
||||
// FIXME p.EntryPoint = static.DefaultInternalEntryPointName
|
||||
}
|
||||
|
||||
// Datadog contains address and metrics pushing interval configuration
|
||||
|
|
21
vendor/github.com/ryanuber/go-glob/LICENSE
generated
vendored
21
vendor/github.com/ryanuber/go-glob/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Ryan Uber
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
56
vendor/github.com/ryanuber/go-glob/glob.go
generated
vendored
56
vendor/github.com/ryanuber/go-glob/glob.go
generated
vendored
|
@ -1,56 +0,0 @@
|
|||
package glob
|
||||
|
||||
import "strings"
|
||||
|
||||
// The character which is treated like a glob
|
||||
const GLOB = "*"
|
||||
|
||||
// Glob will test a string pattern, potentially containing globs, against a
|
||||
// subject string. The result is a simple true/false, determining whether or
|
||||
// not the glob pattern matched the subject text.
|
||||
func Glob(pattern, subj string) bool {
|
||||
// Empty pattern can only match empty subject
|
||||
if pattern == "" {
|
||||
return subj == pattern
|
||||
}
|
||||
|
||||
// If the pattern _is_ a glob, it matches everything
|
||||
if pattern == GLOB {
|
||||
return true
|
||||
}
|
||||
|
||||
parts := strings.Split(pattern, GLOB)
|
||||
|
||||
if len(parts) == 1 {
|
||||
// No globs in pattern, so test for equality
|
||||
return subj == pattern
|
||||
}
|
||||
|
||||
leadingGlob := strings.HasPrefix(pattern, GLOB)
|
||||
trailingGlob := strings.HasSuffix(pattern, GLOB)
|
||||
end := len(parts) - 1
|
||||
|
||||
// Go over the leading parts and ensure they match.
|
||||
for i := 0; i < end; i++ {
|
||||
idx := strings.Index(subj, parts[i])
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
// Check the first section. Requires special handling.
|
||||
if !leadingGlob && idx != 0 {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// Check that the middle parts match.
|
||||
if idx < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Trim evaluated text from subj as we loop over the pattern.
|
||||
subj = subj[idx+len(parts[i]):]
|
||||
}
|
||||
|
||||
// Reached the last section. Requires special handling.
|
||||
return trailingGlob || strings.HasSuffix(subj, parts[end])
|
||||
}
|
8
vendor/github.com/vulcand/predicate/lib.go
generated
vendored
8
vendor/github.com/vulcand/predicate/lib.go
generated
vendored
|
@ -119,6 +119,14 @@ func Or(a, b BoolPredicate) BoolPredicate {
|
|||
}
|
||||
}
|
||||
|
||||
// Not is a boolean predicate that calls a boolean predicate
|
||||
// and returns negated result
|
||||
func Not(a BoolPredicate) BoolPredicate {
|
||||
return func() bool {
|
||||
return !a()
|
||||
}
|
||||
}
|
||||
|
||||
// GetFieldByTag returns a field from the object based on the tag
|
||||
func GetFieldByTag(ival interface{}, tagName string, fieldNames []string) (interface{}, error) {
|
||||
if len(fieldNames) == 0 {
|
||||
|
|
26
vendor/github.com/vulcand/predicate/parse.go
generated
vendored
26
vendor/github.com/vulcand/predicate/parse.go
generated
vendored
|
@ -59,6 +59,16 @@ func (p *predicateParser) parseNode(node ast.Node) (interface{}, error) {
|
|||
return callFunction(fn, arguments)
|
||||
case *ast.ParenExpr:
|
||||
return p.parseNode(n.X)
|
||||
case *ast.UnaryExpr:
|
||||
joinFn, err := p.getJoinFunction(n.Op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node, err := p.parseNode(n.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return callFunction(joinFn, []interface{}{node})
|
||||
}
|
||||
return nil, trace.BadParameter("unsupported %T", node)
|
||||
}
|
||||
|
@ -122,6 +132,20 @@ func (p *predicateParser) evaluateExpr(n ast.Expr) (interface{}, error) {
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return val, nil
|
||||
case *ast.CallExpr:
|
||||
name, err := getIdentifier(l.Fun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fn, err := p.getFunction(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arguments, err := p.evaluateArguments(l.Args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return callFunction(fn, arguments)
|
||||
default:
|
||||
return nil, trace.BadParameter("%T is not supported", n)
|
||||
}
|
||||
|
@ -161,6 +185,8 @@ func (p *predicateParser) joinPredicates(op token.Token, a, b interface{}) (inte
|
|||
func (p *predicateParser) getJoinFunction(op token.Token) (interface{}, error) {
|
||||
var fn interface{}
|
||||
switch op {
|
||||
case token.NOT:
|
||||
fn = p.d.Operators.NOT
|
||||
case token.LAND:
|
||||
fn = p.d.Operators.AND
|
||||
case token.LOR:
|
||||
|
|
18
vendor/github.com/vulcand/predicate/predicate.go
generated
vendored
18
vendor/github.com/vulcand/predicate/predicate.go
generated
vendored
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2014-2018 Vulcand Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Predicate package used to create interpreted mini languages with Go syntax - mostly to define
|
||||
various predicates for configuration, e.g. Latency() > 40 || ErrorRate() > 0.5.
|
||||
|
@ -76,6 +93,7 @@ type Operators struct {
|
|||
|
||||
OR interface{}
|
||||
AND interface{}
|
||||
NOT interface{}
|
||||
}
|
||||
|
||||
// Parser takes the string with expression and calls the operators and functions.
|
||||
|
|
Loading…
Reference in a new issue