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"
|
pruneopts = "NUT"
|
||||||
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
|
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:09d61699d553a4e6ec998ad29816177b1f3d3ed0c18fe923d2c174ec065c99c8"
|
|
||||||
name = "github.com/ryanuber/go-glob"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "256dc444b735e061061cf46c809487313d5b0065"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a"
|
digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a"
|
||||||
name = "github.com/sacloud/libsacloud"
|
name = "github.com/sacloud/libsacloud"
|
||||||
|
@ -1598,12 +1590,12 @@
|
||||||
revision = "3d629cff40b7040e0519628e7774ed11a95d9aff"
|
revision = "3d629cff40b7040e0519628e7774ed11a95d9aff"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:ca6bac407fedc14fbeeba861dd33a821ba3a1624c10126ec6003b0a28d4139c5"
|
digest = "1:b9d8cc221fb40078c7eb78d73b1702b5b548511b3d62bbd56b2f8180089c79af"
|
||||||
name = "github.com/vulcand/predicate"
|
name = "github.com/vulcand/predicate"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "939c094524d124c55fa8afe0e077701db4a865e2"
|
revision = "8fbfb3ab0e94276b6b58bec378600829adc7a203"
|
||||||
version = "v1.0.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -2289,7 +2281,6 @@
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||||
"github.com/prometheus/client_model/go",
|
"github.com/prometheus/client_model/go",
|
||||||
"github.com/rancher/go-rancher-metadata/metadata",
|
"github.com/rancher/go-rancher-metadata/metadata",
|
||||||
"github.com/ryanuber/go-glob",
|
|
||||||
"github.com/sirupsen/logrus",
|
"github.com/sirupsen/logrus",
|
||||||
"github.com/stretchr/testify/assert",
|
"github.com/stretchr/testify/assert",
|
||||||
"github.com/stretchr/testify/mock",
|
"github.com/stretchr/testify/mock",
|
||||||
|
|
|
@ -146,10 +146,6 @@ required = [
|
||||||
name = "github.com/rancher/go-rancher-metadata"
|
name = "github.com/rancher/go-rancher-metadata"
|
||||||
source = "github.com/containous/go-rancher-metadata"
|
source = "github.com/containous/go-rancher-metadata"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/ryanuber/go-glob"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/Masterminds/sprig"
|
name = "github.com/Masterminds/sprig"
|
||||||
version = "2.19.0"
|
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 fully dynamic routing configuration (referred to as the _dynamic configuration_)
|
||||||
- The startup configuration (referred to as the _static 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.
|
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"
|
!!! 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.
|
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| ]]
|
traefik [--flag[=true|false| ]] [-f [true|false| ]]
|
||||||
```
|
```
|
||||||
|
|
||||||
### healthcheck
|
### `healthcheck`
|
||||||
|
|
||||||
Calls Traefik `/ping` to check the health of Traefik.
|
Calls Traefik `/ping` to check the health of Traefik.
|
||||||
Its exit status is `0` if Traefik is healthy and `1` otherwise.
|
Its exit status is `0` if Traefik is healthy and `1` otherwise.
|
||||||
|
@ -50,12 +50,12 @@ $ traefik healthcheck
|
||||||
OK: http://:8082/ping
|
OK: http://:8082/ping
|
||||||
```
|
```
|
||||||
|
|
||||||
### version
|
### `version`
|
||||||
|
|
||||||
Shows the current Traefik version.
|
Shows the current Traefik version.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
```bash
|
```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)
|
Enabling the docker provider (Swarm Mode)
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[docker]
|
[providers.docker]
|
||||||
# swarm classic (1.12-)
|
# swarm classic (1.12-)
|
||||||
# endpoint = "tcp://127.0.0.1:2375"
|
# endpoint = "tcp://127.0.0.1:2375"
|
||||||
# docker swarm mode (1.12+)
|
# 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.
|
and the template has access to all the labels defined on this container.
|
||||||
|
|
||||||
```toml tab="File"
|
```toml tab="File"
|
||||||
[docker]
|
[providers.docker]
|
||||||
defaultRule = ""
|
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -215,6 +215,48 @@ _Optional, Default=15_
|
||||||
|
|
||||||
Defines the polling interval (in seconds) in Swarm Mode.
|
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
|
## Routing Configuration Options
|
||||||
|
|
||||||
### General
|
### 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`.
|
This option overrides the value of `exposedByDefault`.
|
||||||
|
|
||||||
#### `traefik.tags`
|
|
||||||
|
|
||||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
|
||||||
|
|
||||||
#### `traefik.docker.network`
|
#### `traefik.docker.network`
|
||||||
|
|
||||||
Overrides the default docker network to use for connections to the container.
|
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.
|
If set, it overrides the Authorization header.
|
||||||
|
|
||||||
```toml tab="File"
|
```toml tab="File"
|
||||||
[marathon]
|
[providers.marathon]
|
||||||
dcosToken = "xxxxxx"
|
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.
|
and the template has access to all the labels defined on this Marathon application.
|
||||||
|
|
||||||
```toml tab="File"
|
```toml tab="File"
|
||||||
[marathon]
|
[providers.marathon]
|
||||||
defaultRule = ""
|
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ Marathon server endpoint.
|
||||||
You can optionally specify multiple endpoints:
|
You can optionally specify multiple endpoints:
|
||||||
|
|
||||||
```toml tab="File"
|
```toml tab="File"
|
||||||
[marathon]
|
[providers.marathon]
|
||||||
endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
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.
|
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,
|
??? example "Constraints Expression Examples"
|
||||||
e.g. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":".
|
|
||||||
|
```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`
|
### `forceTaskHostname`
|
||||||
|
|
||||||
|
@ -318,10 +361,6 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
Setting this option controls whether Traefik exposes the application.
|
Setting this option controls whether Traefik exposes the application.
|
||||||
It overrides the value of `exposedByDefault`.
|
It overrides the value of `exposedByDefault`.
|
||||||
|
|
||||||
#### `traefik.tags`
|
|
||||||
|
|
||||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
|
||||||
|
|
||||||
#### `traefik.marathon.ipadressidx`
|
#### `traefik.marathon.ipadressidx`
|
||||||
|
|
||||||
If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select.
|
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.
|
Below is the list of the currently supported providers in Traefik.
|
||||||
|
|
||||||
| Provider | Type | Configuration Type |
|
| Provider | Type | Configuration Type |
|
||||||
|---------------------------------|--------------|--------------------|
|
|-----------------------------------|--------------|--------------------|
|
||||||
| [Docker](./docker.md) | Orchestrator | Label |
|
| [Docker](./docker.md) | Orchestrator | Label |
|
||||||
| [File](./file.md) | Orchestrator | Custom Annotation |
|
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource |
|
||||||
| [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource |
|
| [Marathon](./marathon.md) | Orchestrator | Label |
|
||||||
| [Marathon](marathon.md) | Orchestrator | Label |
|
| [Rancher](./rancher.md) | Orchestrator | Label |
|
||||||
|
| [File](./file.md) | Manual | TOML format |
|
||||||
|
|
||||||
!!! note "More Providers"
|
!!! 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.
|
By default Traefik will create routes for all detected containers.
|
||||||
Doing so, Traefik will create routes for containers that match these constraints only.
|
|
||||||
|
|
||||||
??? 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
|
### `exposedByDefault` and `traefik.enable`
|
||||||
constraints = ["tag==api"]
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Containers without the api Tag"
|
List of providers that support that feature:
|
||||||
|
|
||||||
```toml
|
- [Docker](./docker.md#exposedbydefault)
|
||||||
constraints = ["tag!=api"]
|
- [Rancher](./rancher.md#exposedbydefault)
|
||||||
```
|
- [Marathon](./marathon.md#exposedbydefault)
|
||||||
|
|
||||||
??? example "Containers with tags starting with 'us-'"
|
### Constraints
|
||||||
|
|
||||||
```toml
|
List of providers that support constraints:
|
||||||
constraints = ["tag==us-*"]
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Multiple constraints"
|
- [Docker](./docker.md#constraints)
|
||||||
|
- [Rancher](./rancher.md#constraints)
|
||||||
```toml
|
- [Marathon](./marathon.md#constraints)
|
||||||
# Multiple constraints
|
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)
|
||||||
# - "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"]
|
|
||||||
```
|
|
||||||
|
|
|
@ -19,64 +19,23 @@ Attach labels to your services and let Traefik do the rest!
|
||||||
Enabling the rancher provider
|
Enabling the rancher provider
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[provider.rancher]
|
[Providers.Rancher]
|
||||||
```
|
```
|
||||||
|
|
||||||
Attaching labels to services
|
Attaching labels to services
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
- traefik.http.services.my-service.rule=Host(my-domain)
|
- traefik.http.services.my-service.rule=Host(`my-domain`)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Provider Configuration Options
|
## 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:
|
If you're in a hurry, maybe you'd rather go through the configuration reference:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
################################################################
|
--8<-- "content/providers/rancher.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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `ExposedByDefault`
|
### `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.
|
and the template has access to all the labels defined on this container.
|
||||||
|
|
||||||
```toml tab="File"
|
```toml tab="File"
|
||||||
[rancher]
|
[Providers.Rancher]
|
||||||
defaultRule = ""
|
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -136,6 +95,50 @@ _Optional, Default=/latest_
|
||||||
|
|
||||||
Prefix used for accessing the Rancher metadata service
|
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
|
### General
|
||||||
|
|
||||||
Traefik creates, for each rancher service, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md).
|
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`.
|
This option overrides the value of `exposedByDefault`.
|
||||||
|
|
||||||
#### `traefik.tags`
|
|
||||||
|
|
||||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
|
||||||
|
|
||||||
#### Port Lookup
|
#### Port Lookup
|
||||||
|
|
||||||
Traefik is now capable of detecting the port to use, by following the default rancher flow.
|
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.
|
Enable Docker backend with default settings.
|
||||||
|
|
||||||
--providers.docker.constraints (Default: "")
|
--providers.docker.constraints (Default: "")
|
||||||
Filter services by constraint, matching with Traefik tags.
|
Constraints is an expression that Traefik matches against the container's labels
|
||||||
|
to determine whether to create any route for that container.
|
||||||
--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.
|
|
||||||
|
|
||||||
--providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
--providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||||
Default rule.
|
Default rule.
|
||||||
|
@ -382,17 +373,8 @@
|
||||||
Basic authentication Password.
|
Basic authentication Password.
|
||||||
|
|
||||||
--providers.marathon.constraints (Default: "")
|
--providers.marathon.constraints (Default: "")
|
||||||
Filter services by constraint, matching with Traefik tags.
|
Constraints is an expression that Traefik matches against the application's
|
||||||
|
labels to determine whether to create any route for that application.
|
||||||
--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.
|
|
||||||
|
|
||||||
--providers.marathon.dcostoken (Default: "")
|
--providers.marathon.dcostoken (Default: "")
|
||||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
DCOSToken for DCOS environment, This will override the Authorization header.
|
||||||
|
@ -409,9 +391,6 @@
|
||||||
--providers.marathon.exposedbydefault (Default: "true")
|
--providers.marathon.exposedbydefault (Default: "true")
|
||||||
Expose Marathon apps by default.
|
Expose Marathon apps by default.
|
||||||
|
|
||||||
--providers.marathon.filtermarathonconstraints (Default: "false")
|
|
||||||
Enable use of Marathon constraints in constraint filtering.
|
|
||||||
|
|
||||||
--providers.marathon.forcetaskhostname (Default: "false")
|
--providers.marathon.forcetaskhostname (Default: "false")
|
||||||
Force to use the task's hostname.
|
Force to use the task's hostname.
|
||||||
|
|
||||||
|
@ -457,17 +436,8 @@
|
||||||
Enable Rancher backend with default settings.
|
Enable Rancher backend with default settings.
|
||||||
|
|
||||||
--providers.rancher.constraints (Default: "")
|
--providers.rancher.constraints (Default: "")
|
||||||
Filter services by constraint, matching with Traefik tags.
|
Constraints is an expression that Traefik matches against the container's labels
|
||||||
|
to determine whether to create any route for that container.
|
||||||
--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.
|
|
||||||
|
|
||||||
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||||
Default rule.
|
Default rule.
|
||||||
|
|
|
@ -241,16 +241,7 @@ Middleware list.
|
||||||
Enable Docker backend with default settings. (Default: ```false```)
|
Enable Docker backend with default settings. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
|
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
|
||||||
Filter services by constraint, matching with Traefik tags.
|
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_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.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
|
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
|
||||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||||
|
@ -373,16 +364,7 @@ Basic authentication User.
|
||||||
Basic authentication Password.
|
Basic authentication Password.
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
|
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
|
||||||
Filter services by constraint, matching with Traefik tags.
|
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_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.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
|
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
|
||||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
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`:
|
`TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`:
|
||||||
Expose Marathon apps by default. (Default: ```true```)
|
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`:
|
`TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`:
|
||||||
Force to use the task's hostname. (Default: ```false```)
|
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```)
|
Enable Rancher backend with default settings. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
|
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
|
||||||
Filter services by constraint, matching with Traefik tags.
|
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_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.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
|
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
|
||||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||||
|
|
|
@ -41,16 +41,7 @@
|
||||||
SwarmMode = true
|
SwarmMode = true
|
||||||
Network = "foobar"
|
Network = "foobar"
|
||||||
SwarmModeRefreshSeconds = 42
|
SwarmModeRefreshSeconds = 42
|
||||||
|
Constraints = "foobar"
|
||||||
[[Providers.Docker.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[[Providers.Docker.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[Providers.Docker.TLS]
|
[Providers.Docker.TLS]
|
||||||
CA = "foobar"
|
CA = "foobar"
|
||||||
|
@ -73,23 +64,13 @@
|
||||||
DefaultRule = "foobar"
|
DefaultRule = "foobar"
|
||||||
ExposedByDefault = true
|
ExposedByDefault = true
|
||||||
DCOSToken = "foobar"
|
DCOSToken = "foobar"
|
||||||
FilterMarathonConstraints = true
|
|
||||||
DialerTimeout = 42
|
DialerTimeout = 42
|
||||||
ResponseHeaderTimeout = 42
|
ResponseHeaderTimeout = 42
|
||||||
TLSHandshakeTimeout = 42
|
TLSHandshakeTimeout = 42
|
||||||
KeepAlive = 42
|
KeepAlive = 42
|
||||||
ForceTaskHostname = true
|
ForceTaskHostname = true
|
||||||
RespectReadinessChecks = true
|
RespectReadinessChecks = true
|
||||||
|
Constraints = "foobar"
|
||||||
[[Providers.Marathon.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[[Providers.Marathon.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[Providers.Marathon.TLS]
|
[Providers.Marathon.TLS]
|
||||||
CA = "foobar"
|
CA = "foobar"
|
||||||
|
@ -134,16 +115,7 @@
|
||||||
RefreshSeconds = 42
|
RefreshSeconds = 42
|
||||||
IntervalPoll = true
|
IntervalPoll = true
|
||||||
Prefix = "foobar"
|
Prefix = "foobar"
|
||||||
|
Constraints = "foobar"
|
||||||
[[Providers.Rancher.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[[Providers.Rancher.Constraints]]
|
|
||||||
Key = "foobar"
|
|
||||||
MustMatch = true
|
|
||||||
Regex = "foobar"
|
|
||||||
|
|
||||||
[API]
|
[API]
|
||||||
EntryPoint = "foobar"
|
EntryPoint = "foobar"
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config/static"
|
"github.com/containous/traefik/pkg/config/static"
|
||||||
"github.com/containous/traefik/pkg/ping"
|
"github.com/containous/traefik/pkg/ping"
|
||||||
"github.com/containous/traefik/pkg/provider"
|
|
||||||
"github.com/containous/traefik/pkg/provider/acme"
|
"github.com/containous/traefik/pkg/provider/acme"
|
||||||
acmeprovider "github.com/containous/traefik/pkg/provider/acme"
|
acmeprovider "github.com/containous/traefik/pkg/provider/acme"
|
||||||
"github.com/containous/traefik/pkg/provider/docker"
|
"github.com/containous/traefik/pkg/provider/docker"
|
||||||
|
@ -153,20 +152,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Providers.Docker = &docker.Provider{
|
config.Providers.Docker = &docker.Provider{
|
||||||
Constrainer: provider.Constrainer{
|
Constraints: `Label("foo", "bar")`,
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Watch: true,
|
Watch: true,
|
||||||
Endpoint: "MyEndPoint",
|
Endpoint: "MyEndPoint",
|
||||||
DefaultRule: "PathPrefix(`/`)",
|
DefaultRule: "PathPrefix(`/`)",
|
||||||
|
|
|
@ -248,7 +248,6 @@ func Test_decodeFileToNode_Toml(t *testing.T) {
|
||||||
{Name: "DialerTimeout", Value: "42"},
|
{Name: "DialerTimeout", Value: "42"},
|
||||||
{Name: "Endpoint", Value: "foobar"},
|
{Name: "Endpoint", Value: "foobar"},
|
||||||
{Name: "ExposedByDefault", Value: "true"},
|
{Name: "ExposedByDefault", Value: "true"},
|
||||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
|
||||||
{Name: "ForceTaskHostname", Value: "true"},
|
{Name: "ForceTaskHostname", Value: "true"},
|
||||||
{Name: "KeepAlive", Value: "42"},
|
{Name: "KeepAlive", Value: "42"},
|
||||||
{Name: "RespectReadinessChecks", Value: "true"},
|
{Name: "RespectReadinessChecks", Value: "true"},
|
||||||
|
@ -518,7 +517,6 @@ func Test_decodeFileToNode_Yaml(t *testing.T) {
|
||||||
{Name: "DialerTimeout", Value: "42"},
|
{Name: "DialerTimeout", Value: "42"},
|
||||||
{Name: "Endpoint", Value: "foobar"},
|
{Name: "Endpoint", Value: "foobar"},
|
||||||
{Name: "ExposedByDefault", Value: "true"},
|
{Name: "ExposedByDefault", Value: "true"},
|
||||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
|
||||||
{Name: "ForceTaskHostname", Value: "true"},
|
{Name: "ForceTaskHostname", Value: "true"},
|
||||||
{Name: "KeepAlive", Value: "42"},
|
{Name: "KeepAlive", Value: "42"},
|
||||||
{Name: "RespectReadinessChecks", Value: "true"},
|
{Name: "RespectReadinessChecks", Value: "true"},
|
||||||
|
|
|
@ -74,7 +74,6 @@
|
||||||
DefaultRule = "foobar"
|
DefaultRule = "foobar"
|
||||||
ExposedByDefault = true
|
ExposedByDefault = true
|
||||||
DCOSToken = "foobar"
|
DCOSToken = "foobar"
|
||||||
FilterMarathonConstraints = true
|
|
||||||
DialerTimeout = 42
|
DialerTimeout = 42
|
||||||
ResponseHeaderTimeout = 42
|
ResponseHeaderTimeout = 42
|
||||||
TLSHandshakeTimeout = 42
|
TLSHandshakeTimeout = 42
|
||||||
|
|
|
@ -69,7 +69,6 @@ Providers:
|
||||||
DefaultRule: foobar
|
DefaultRule: foobar
|
||||||
ExposedByDefault: true
|
ExposedByDefault: true
|
||||||
DCOSToken: foobar
|
DCOSToken: foobar
|
||||||
FilterMarathonConstraints: true
|
|
||||||
DialerTimeout: 42
|
DialerTimeout: 42
|
||||||
ResponseHeaderTimeout: 42
|
ResponseHeaderTimeout: 42
|
||||||
TLSHandshakeTimeout: 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/config/label"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/provider"
|
"github.com/containous/traefik/pkg/provider"
|
||||||
|
"github.com/containous/traefik/pkg/provider/constraints"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -123,10 +124,13 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, failingConstraint := p.MatchConstraints(container.ExtraConf.Tags); !ok {
|
matches, err := constraints.Match(container.Labels, p.Constraints)
|
||||||
if failingConstraint != nil {
|
if err != nil {
|
||||||
logger.Debugf("Container pruned by %q constraint", failingConstraint.String())
|
logger.Error("Error matching constraints expression: %v", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
if !matches {
|
||||||
|
logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/types"
|
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
@ -339,7 +338,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
containers []dockerData
|
containers []dockerData
|
||||||
constraints []*types.Constraint
|
constraints string
|
||||||
expected *config.Configuration
|
expected *config.Configuration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -1924,13 +1923,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: []*types.Constraint{
|
constraints: `Label("traefik.tags", "bar")`,
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -1965,13 +1958,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: []*types.Constraint{
|
constraints: `Label("traefik.tags", "foo")`,
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
|
|
@ -45,7 +45,7 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
// Provider holds configurations of the provider.
|
// Provider holds configurations of the provider.
|
||||||
type Provider struct {
|
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"`
|
Watch bool `description:"Watch provider." export:"true"`
|
||||||
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."`
|
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."`
|
||||||
DefaultRule string `description:"Default rule."`
|
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.
|
// configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider.
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Tags []string
|
|
||||||
Docker specificConfiguration
|
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 {
|
if err != nil {
|
||||||
return configuration{}, err
|
return configuration{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/containous/traefik/pkg/config/label"
|
"github.com/containous/traefik/pkg/config/label"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/provider"
|
"github.com/containous/traefik/pkg/provider"
|
||||||
|
"github.com/containous/traefik/pkg/provider/constraints"
|
||||||
"github.com/gambol99/go-marathon"
|
"github.com/gambol99/go-marathon"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,11 +30,20 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
|
||||||
continue
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
confFromLabel, err := label.DecodeConfiguration(stringValueMap(app.Labels))
|
confFromLabel, err := label.DecodeConfiguration(labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
continue
|
continue
|
||||||
|
@ -65,7 +75,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
}{
|
}{
|
||||||
Name: app.ID,
|
Name: app.ID,
|
||||||
Labels: stringValueMap(app.Labels),
|
Labels: labels,
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := getServiceName(app)
|
serviceName := getServiceName(app)
|
||||||
|
@ -164,7 +174,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
|
||||||
return nil
|
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)
|
logger := log.FromContext(ctx)
|
||||||
|
|
||||||
// Filter disabled application.
|
// Filter disabled application.
|
||||||
|
@ -174,10 +184,13 @@ func (p *Provider) keepApplication(ctx context.Context, extraConf configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by constraints.
|
// Filter by constraints.
|
||||||
if ok, failingConstraint := p.MatchConstraints(extraConf.Tags); !ok {
|
matches, err := constraints.Match(labels, p.Constraints)
|
||||||
if failingConstraint != nil {
|
if err != nil {
|
||||||
logger.Debugf("Filtering Marathon application, pruned by %q constraint", failingConstraint.String())
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/types"
|
|
||||||
"github.com/gambol99/go-marathon"
|
"github.com/gambol99/go-marathon"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -31,8 +30,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
applications *marathon.Applications
|
applications *marathon.Applications
|
||||||
constraints []*types.Constraint
|
constraints string
|
||||||
filterMarathonConstraints bool
|
|
||||||
defaultRule string
|
defaultRule string
|
||||||
expected *config.Configuration
|
expected *config.Configuration
|
||||||
}{
|
}{
|
||||||
|
@ -1065,13 +1063,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withTasks(localhostTask(taskPorts(80, 81))),
|
withTasks(localhostTask(taskPorts(80, 81))),
|
||||||
withLabel("traefik.tags", "foo"),
|
withLabel("traefik.tags", "foo"),
|
||||||
)),
|
)),
|
||||||
constraints: []*types.Constraint{
|
constraints: `Label("traefik.tags", "bar")`,
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -1093,14 +1085,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withTasks(localhostTask(taskPorts(80, 81))),
|
withTasks(localhostTask(taskPorts(80, 81))),
|
||||||
constraint("rack_id:CLUSTER:rack-1"),
|
constraint("rack_id:CLUSTER:rack-1"),
|
||||||
)),
|
)),
|
||||||
filterMarathonConstraints: true,
|
constraints: `MarathonConstraint("rack_id:CLUSTER:rack-2")`,
|
||||||
constraints: []*types.Constraint{
|
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "rack_id:CLUSTER:rack-2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -1122,14 +1107,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withTasks(localhostTask(taskPorts(80, 81))),
|
withTasks(localhostTask(taskPorts(80, 81))),
|
||||||
constraint("rack_id:CLUSTER:rack-1"),
|
constraint("rack_id:CLUSTER:rack-1"),
|
||||||
)),
|
)),
|
||||||
filterMarathonConstraints: true,
|
constraints: `MarathonConstraint("rack_id:CLUSTER:rack-1")`,
|
||||||
constraints: []*types.Constraint{
|
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "rack_id:CLUSTER:rack-1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -1167,14 +1145,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withTasks(localhostTask(taskPorts(80, 81))),
|
withTasks(localhostTask(taskPorts(80, 81))),
|
||||||
withLabel("traefik.tags", "bar"),
|
withLabel("traefik.tags", "bar"),
|
||||||
)),
|
)),
|
||||||
|
constraints: `Label("traefik.tags", "bar")`,
|
||||||
constraints: []*types.Constraint{
|
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -1419,7 +1390,6 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
p := &Provider{
|
p := &Provider{
|
||||||
DefaultRule: defaultRule,
|
DefaultRule: defaultRule,
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
FilterMarathonConstraints: test.filterMarathonConstraints,
|
|
||||||
}
|
}
|
||||||
p.Constraints = test.constraints
|
p.Constraints = test.constraints
|
||||||
|
|
||||||
|
@ -1473,7 +1443,7 @@ func TestApplicationFilterEnabled(t *testing.T) {
|
||||||
extraConf, err := provider.getConfiguration(app)
|
extraConf, err := provider.getConfiguration(app)
|
||||||
require.NoError(t, err)
|
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)
|
t.Errorf("got unexpected filtering = %t", !test.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,6 @@ package marathon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config/label"
|
"github.com/containous/traefik/pkg/config/label"
|
||||||
"github.com/gambol99/go-marathon"
|
"github.com/gambol99/go-marathon"
|
||||||
|
@ -10,7 +9,6 @@ import (
|
||||||
|
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Tags []string
|
|
||||||
Marathon specificConfiguration
|
Marathon specificConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,23 +21,16 @@ func (p *Provider) getConfiguration(app marathon.Application) (configuration, er
|
||||||
|
|
||||||
conf := configuration{
|
conf := configuration{
|
||||||
Enable: p.ExposedByDefault,
|
Enable: p.ExposedByDefault,
|
||||||
Tags: nil,
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
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 {
|
if err != nil {
|
||||||
return configuration{}, err
|
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
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: false,
|
ExposedByDefault: false,
|
||||||
FilterMarathonConstraints: false,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Tags: nil,
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
IPAddressIdx: math.MinInt32,
|
||||||
},
|
},
|
||||||
|
@ -44,11 +42,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: false,
|
ExposedByDefault: false,
|
||||||
FilterMarathonConstraints: false,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Tags: nil,
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
IPAddressIdx: math.MinInt32,
|
||||||
},
|
},
|
||||||
|
@ -64,11 +60,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: false,
|
ExposedByDefault: false,
|
||||||
FilterMarathonConstraints: false,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Tags: nil,
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: 4,
|
IPAddressIdx: 4,
|
||||||
},
|
},
|
||||||
|
@ -84,13 +78,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: false,
|
ExposedByDefault: false,
|
||||||
FilterMarathonConstraints: true,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Tags: []string{
|
|
||||||
"key:value",
|
|
||||||
},
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
IPAddressIdx: math.MinInt32,
|
||||||
},
|
},
|
||||||
|
@ -104,11 +94,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
FilterMarathonConstraints: false,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Tags: nil,
|
|
||||||
Marathon: specificConfiguration{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
IPAddressIdx: math.MinInt32,
|
||||||
},
|
},
|
||||||
|
@ -124,31 +112,9 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
p: Provider{
|
p: Provider{
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
FilterMarathonConstraints: false,
|
|
||||||
},
|
},
|
||||||
expected: configuration{
|
expected: configuration{
|
||||||
Enable: false,
|
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{
|
Marathon: specificConfiguration{
|
||||||
IPAddressIdx: math.MinInt32,
|
IPAddressIdx: math.MinInt32,
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,15 +45,13 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
// Provider holds configuration of the provider.
|
// Provider holds configuration of the provider.
|
||||||
type Provider struct {
|
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"`
|
Trace bool `description:"Display additional provider logs." export:"true"`
|
||||||
Watch bool `description:"Watch provider." 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"`
|
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon." export:"true"`
|
||||||
DefaultRule string `description:"Default rule."`
|
DefaultRule string `description:"Default rule."`
|
||||||
ExposedByDefault bool `description:"Expose Marathon apps by default." export:"true"`
|
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"`
|
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"`
|
TLS *types.ClientTLS `description:"Enable TLS support." export:"true"`
|
||||||
DialerTimeout types.Duration `description:"Set a dialer timeout for Marathon." 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"`
|
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/config/label"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/provider"
|
"github.com/containous/traefik/pkg/provider"
|
||||||
|
"github.com/containous/traefik/pkg/provider/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Provider) buildConfiguration(ctx context.Context, services []rancherData) *config.Configuration {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, failingConstraint := p.MatchConstraints(service.ExtraConf.Tags); !ok {
|
matches, err := constraints.Match(service.Labels, p.Constraints)
|
||||||
if failingConstraint != nil {
|
if err != nil {
|
||||||
logger.Debugf("service pruned by %q constraint", failingConstraint.String())
|
logger.Error("Error matching constraints expression: %v", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
if !matches {
|
||||||
|
logger.Debugf("Service pruned by constraint expression: %q", p.Constraints)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -14,7 +13,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
containers []rancherData
|
containers []rancherData
|
||||||
constraints []*types.Constraint
|
constraints string
|
||||||
expected *config.Configuration
|
expected *config.Configuration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -330,13 +329,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
State: "",
|
State: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: []*types.Constraint{
|
constraints: `Label("traefik.tags", "bar")`,
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
@ -363,13 +356,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
State: "",
|
State: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: []*types.Constraint{
|
constraints: `Label("traefik.tags", "foo")`,
|
||||||
{
|
|
||||||
Key: "tag",
|
|
||||||
MustMatch: true,
|
|
||||||
Value: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &config.Configuration{
|
expected: &config.Configuration{
|
||||||
TCP: &config.TCPConfiguration{
|
TCP: &config.TCPConfiguration{
|
||||||
Routers: map[string]*config.TCPRouter{},
|
Routers: map[string]*config.TCPRouter{},
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Tags []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getConfiguration(service rancherData) (configuration, error) {
|
func (p *Provider) getConfiguration(service rancherData) (configuration, error) {
|
||||||
|
@ -14,7 +13,7 @@ func (p *Provider) getConfiguration(service rancherData) (configuration, error)
|
||||||
Enable: p.ExposedByDefault,
|
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 {
|
if err != nil {
|
||||||
return configuration{}, err
|
return configuration{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,7 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
// Provider holds configurations of the provider.
|
// Provider holds configurations of the provider.
|
||||||
type Provider struct {
|
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"`
|
Watch bool `description:"Watch provider." export:"true"`
|
||||||
DefaultRule string `description:"Default rule."`
|
DefaultRule string `description:"Default rule."`
|
||||||
ExposedByDefault bool `description:"Expose containers by default." export:"true"`
|
ExposedByDefault bool `description:"Expose containers by default." export:"true"`
|
||||||
|
|
|
@ -25,7 +25,6 @@ type Provider struct {
|
||||||
// SetDefaults sets the default values.
|
// SetDefaults sets the default values.
|
||||||
func (p *Provider) SetDefaults() {
|
func (p *Provider) SetDefaults() {
|
||||||
p.EntryPoint = "traefik"
|
p.EntryPoint = "traefik"
|
||||||
// FIXME p.EntryPoint = static.DefaultInternalEntryPointName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var templatesRenderer = render.New(render.Options{Directory: "nowhere"})
|
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() {
|
func (p *Prometheus) SetDefaults() {
|
||||||
p.Buckets = []float64{0.1, 0.3, 1.2, 5}
|
p.Buckets = []float64{0.1, 0.3, 1.2, 5}
|
||||||
p.EntryPoint = "traefik"
|
p.EntryPoint = "traefik"
|
||||||
// FIXME p.EntryPoint = static.DefaultInternalEntryPointName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Datadog contains address and metrics pushing interval configuration
|
// 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
|
// GetFieldByTag returns a field from the object based on the tag
|
||||||
func GetFieldByTag(ival interface{}, tagName string, fieldNames []string) (interface{}, error) {
|
func GetFieldByTag(ival interface{}, tagName string, fieldNames []string) (interface{}, error) {
|
||||||
if len(fieldNames) == 0 {
|
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)
|
return callFunction(fn, arguments)
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
return p.parseNode(n.X)
|
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)
|
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 nil, trace.Wrap(err)
|
||||||
}
|
}
|
||||||
return val, nil
|
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:
|
default:
|
||||||
return nil, trace.BadParameter("%T is not supported", n)
|
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) {
|
func (p *predicateParser) getJoinFunction(op token.Token) (interface{}, error) {
|
||||||
var fn interface{}
|
var fn interface{}
|
||||||
switch op {
|
switch op {
|
||||||
|
case token.NOT:
|
||||||
|
fn = p.d.Operators.NOT
|
||||||
case token.LAND:
|
case token.LAND:
|
||||||
fn = p.d.Operators.AND
|
fn = p.d.Operators.AND
|
||||||
case token.LOR:
|
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
|
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.
|
various predicates for configuration, e.g. Latency() > 40 || ErrorRate() > 0.5.
|
||||||
|
@ -76,6 +93,7 @@ type Operators struct {
|
||||||
|
|
||||||
OR interface{}
|
OR interface{}
|
||||||
AND interface{}
|
AND interface{}
|
||||||
|
NOT interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser takes the string with expression and calls the operators and functions.
|
// Parser takes the string with expression and calls the operators and functions.
|
||||||
|
|
Loading…
Reference in a new issue