Merge branch 'v2.0' into master

This commit is contained in:
Fernandez Ludovic 2019-06-25 20:16:20 +02:00
commit 15b5433f1a
153 changed files with 3978 additions and 1308 deletions

View file

@ -5,7 +5,7 @@ The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community forum: https://community.containo.us/
-->

View file

@ -11,7 +11,7 @@ The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community forum: https://community.containo.us/
-->

View file

@ -11,7 +11,7 @@ The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community forum: https://community.containo.us/
-->

View file

@ -1,5 +1,25 @@
# Change Log
## [v2.0.0-alpha7](https://github.com/containous/traefik/tree/v2.0.0-alpha7) (2019-06-21)
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha6...v2.0.0-alpha7)
**Enhancements:**
- **[api]** API: new contract ([#4964](https://github.com/containous/traefik/pull/4964) by [mpl](https://github.com/mpl))
- **[k8s,k8s/crd,tls]** Define TLS options on the Router configuration for Kubernetes ([#4973](https://github.com/containous/traefik/pull/4973) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[middleware,provider]** Change the provider separator from . to @ ([#4982](https://github.com/containous/traefik/pull/4982) by [ldez](https://github.com/ldez))
- **[provider]** Use name@provider instead of provider@name. ([#4990](https://github.com/containous/traefik/pull/4990) by [ldez](https://github.com/ldez))
- **[provider]** New constraints management. ([#4965](https://github.com/containous/traefik/pull/4965) by [ldez](https://github.com/ldez))
**Bug fixes:**
- **[cli]** Fix some CLI bugs ([#4989](https://github.com/containous/traefik/pull/4989) by [ldez](https://github.com/ldez))
- **[cli]** Filter env vars configuration ([#4985](https://github.com/containous/traefik/pull/4985) by [ldez](https://github.com/ldez))
- **[cli]** Return an error when help is called on a non existing command. ([#4977](https://github.com/containous/traefik/pull/4977) by [ldez](https://github.com/ldez))
- **[tls]** Fix panic in TLS stores handling ([#4997](https://github.com/containous/traefik/pull/4997) by [juliens](https://github.com/juliens))
**Documentation:**
- **[acme,tls]** docs: rewrite of the HTTPS and TLS section ([#4980](https://github.com/containous/traefik/pull/4980) by [mpl](https://github.com/mpl))
- Improve various parts of the documentation. ([#4996](https://github.com/containous/traefik/pull/4996) by [ldez](https://github.com/ldez))
## [v2.0.0-alpha6](https://github.com/containous/traefik/tree/v2.0.0-alpha6) (2019-06-18)
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha5...v2.0.0-alpha6)

15
Gopkg.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -8,7 +8,7 @@
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](http://goreportcard.com/report/containous/traefik)
[![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md)
[![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
[![Join the community support forum at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
[![Twitter](https://img.shields.io/twitter/follow/traefik.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefik)
@ -103,7 +103,7 @@ A collection of contributions around Traefik can be found at [https://awesome.tr
## Support
To get community support, you can:
- join the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
- join the Traefik community forum: [![Join the chat at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.

View file

@ -44,7 +44,7 @@ func main() {
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}}
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
cmdTraefik := &cli.Command{
Name: "traefik",

View file

@ -28,7 +28,7 @@
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
* During the Contributions Daily Meeting
* By an assigned maintainer
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, etc)
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, Discourse, etc)
## PR review process:

View file

@ -11,10 +11,10 @@ To save us some time and get quicker feedback, be sure to follow the guide lines
!!! important "Getting Help Vs Reporting an Issue"
The issue tracker is not a general support forum, but a place to report bugs and asks for new features.
For end-user related support questions, try using first:
- the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
- the Traefik community forum: [![Join the chat at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
## Issue Title

View file

@ -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.
@ -36,8 +36,8 @@ Traefik gets its _dynamic configuration_ from [providers](../providers/overview.
There are three different, mutually exclusive, ways to define static configuration options in Traefik:
- In a configuration file
- As environment variables
- In the command-line arguments
- As environment variables
These ways are evaluated in the order listed above.
@ -70,6 +70,12 @@ docker run traefik[:version] --help
# ex: docker run traefik:2.0 --help
```
All available arguments can also be found [here](../reference/static-configuration/cli.md).
### Environment Variables
All available environment variables can be found [here](../reference/static-configuration/env.md)
## Available Configuration Options
All the configuration options are documented in their related section.

View file

@ -16,9 +16,13 @@ Pieces of middleware can be combined in chains to fit every scenario.
```yaml tab="Docker"
# As a Docker Label
whoami:
image: containous/whoami # A container that exposes an API to show its IP address
# A container that exposes an API to show its IP address
image: containous/whoami
labels:
# Create a middleware named `foo-add-prefix`
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
# Apply the middleware named `foo-add-prefix` to the router named `router1`
- "traefik.http.router.router1.Middlewares=foo-add-prefix@docker"
```
```yaml tab="Kubernetes"
@ -61,14 +65,44 @@ spec:
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo"
"traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo",
"traefik.http.router.router1.Middlewares": "foo-add-prefix@marathon"
}
```
```yaml tab="Rancher"
# As a Rancher Label
labels:
# Create a middleware named `foo-add-prefix`
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
# Apply the middleware named `foo-add-prefix` to the router named `router1`
- "traefik.http.router.router1.Middlewares=foo-add-prefix@rancher"
```
```yaml tab="Kubernetes"
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: mytlsoption
namespace: default
spec:
minversion: VersionTLS12
```
```toml tab="File"
@ -83,25 +117,30 @@ labels:
Rule = "Host(`example.com`)"
[http.middlewares]
[http.middlewares.foo-add-prefix.AddPrefix]
[http.middlewares.foo-add-prefix.AddPrefix]
prefix = "/foo"
[http.services]
[http.services.service1]
[http.services.service1.LoadBalancer]
[http.services.service1]
[http.services.service1.LoadBalancer]
[[http.services.service1.LoadBalancer.Servers]]
URL = "http://127.0.0.1:80"
[[http.services.service1.LoadBalancer.Servers]]
URL = "http://127.0.0.1:80"
```
## Advanced Configuration
## Provider Namespace
When you declare a middleware, it lives in its `provider` namespace.
For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker `provider` namespace.
When you declare a middleware, it lives in its provider namespace.
For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker provider namespace.
If you use multiple `providers` and wish to reference a middleware declared in another `provider`, then you'll have to prefix the middleware name with the `provider` name.
If you use multiple providers and wish to reference a middleware declared in another provider,
then you'll have to prefix the middleware name with the provider name.
??? abstract "Referencing a Middleware from Another Provider"
```text
<resource-name>@<provider-name>
```
!!! abstract "Referencing a Middleware from Another Provider"
Declaring the add-foo-prefix in the file provider.
@ -121,8 +160,8 @@ If you use multiple `providers` and wish to reference a middleware declared in a
image: your-docker-image
labels:
# Attach file@add-foo-prefix middleware (declared in file)
- "traefik.http.routers.my-container.middlewares=file@add-foo-prefix"
# Attach add-foo-prefix@file middleware (declared in file)
- "traefik.http.routers.my-container.middlewares=add-foo-prefix@file"
```
## Available Middlewares

View file

@ -5,41 +5,57 @@ Reading What's Happening
By default, logs are written to stdout, in text format.
## Configuration Example
??? example "Writing Logs in a File"
```toml
[log]
filePath = "/path/to/traefik.log"
```
??? example "Writing Logs in a File, in JSON"
```toml
[log]
filePath = "/path/to/log-file.log"
format = "json"
```
## Configuration Options
## Configuration
### General
Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on).
#### filePath
#### `filePath`
By default, the logs are written to the standard output.
You can configure a file path instead using the `filePath` option.
#### format
```toml tab="File"
# Writing Logs to a File
[log]
filePath = "/path/to/traefik.log"
```
```bash tab="CLI"
# Writing Logs to a File
--log.filePath="/path/to/traefik.log"
```
#### `format`
By default, the logs use a text format (`common`), but you can also ask for the `json` format in the `format` option.
#### log level
```toml tab="File"
# Writing Logs to a File, in JSON
[log]
filePath = "/path/to/log-file.log"
format = "json"
```
By default, the `level` is set to `error`, but you can choose amongst `debug`, `panic`, `fatal`, `error`, `warn`, and `info`.
```bash tab="CLI"
# Writing Logs to a File, in JSON
--log.filePath="/path/to/traefik.log"
--log.format="json"
```
#### `level`
By default, the `level` is set to `ERROR`. Alternative logging levels are `DEBUG`, `PANIC`, `FATAL`, `ERROR`, `WARN`, and `INFO`.
```toml tab="File"
[log]
level = "DEBUG"
```
```bash tab="CLI"
--log.level="DEBUG"
```
## Log Rotation

View file

@ -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
```

View file

@ -0,0 +1,13 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced

View file

@ -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.

View file

@ -230,6 +230,51 @@ spec:
More information about available middlewares in the dedicated [middlewares section](../middlewares/overview.md).
### Traefik TLS Option Definition
Additionally, to allow for the use of tls options in an IngressRoute, we defined the CRD below for the TLSOption kind.
More information about TLS Options is available in the dedicated [TLS Configuration Options](../../https/tls/#tls-options).
```yaml
--8<-- "content/providers/crd_tls_option.yml"
```
Once the TLSOption kind has been registered with the Kubernetes cluster or defined in the File Provider, it can then be used in IngressRoute definitions, such as:
```yaml
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: mytlsoption
namespace: default
spec:
minversion: VersionTLS12
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutebar
spec:
entryPoints:
- web
routes:
- match: Host(`bar.com`) && PathPrefix(`/stripit`)
kind: Rule
services:
- name: whoami
port: 80
tls:
options:
name: mytlsoption
namespace: default
```
!!! note "TLS Option reference and namespace"
If the optional `namespace` attribute is not set, the configuration will be applied with the namespace of the IngressRoute.
### TLS
To allow for TLS, we made use of the `Secret` kind, as it was already defined, and it can be directly used in an `IngressRoute`:

View file

@ -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.

View file

@ -26,74 +26,46 @@ 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 |
| Provider | Type | Configuration Type |
|-----------------------------------|--------------|--------------------|
| [Docker](./docker.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)
-->
## Constraints Configuration
TODO (document TCP VS HTTP dynamic configuration)
-->
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.
## Restrict the Scope of Service Discovery
??? example "Containers with the api Tag"
By default Traefik will create routes for all detected containers.
```toml
constraints = ["tag==api"]
```
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.
??? example "Containers without the api Tag"
### `exposedByDefault` and `traefik.enable`
```toml
constraints = ["tag!=api"]
```
??? example "Containers with tags starting with 'us-'"
List of providers that support that feature:
```toml
constraints = ["tag==us-*"]
```
- [Docker](./docker.md#exposedbydefault)
- [Rancher](./rancher.md#exposedbydefault)
- [Marathon](./marathon.md#exposedbydefault)
??? example "Multiple constraints"
### Constraints
```toml
# Multiple constraints
# - "tag==" must match with at least one tag
# - "tag!=" must match with none of tags
constraints = ["tag!=us-*", "tag!=asia-*"]
```
List of providers that support constraints:
??? 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)

View file

@ -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.

View 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"

View file

@ -26,6 +26,21 @@ spec:
singular: middleware
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
@ -85,6 +100,9 @@ spec:
# use an empty tls object for TLS with Let's Encrypt
tls:
secretName: supersecret
options:
name: myTLSOption
namespace: default
---
apiVersion: traefik.containo.us/v1alpha1
@ -104,3 +122,6 @@ spec:
tls:
secretName: foosecret
passthrough: false
options:
name: myTLSOption
namespace: default

View file

@ -1,4 +1,3 @@
--accesslog (Default: "false")
Access log settings.
@ -173,6 +172,9 @@
--hostresolver.resolvdepth (Default: "5")
The maximal depth of DNS recursive resolving
--log (Default: "false")
Traefik log settings.
--log.filepath (Default: "")
Traefik log file path. Stdout is used when omitted or empty.
@ -249,17 +251,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 +375,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 +393,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 +438,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.

View file

@ -165,6 +165,9 @@ resolv.conf used for DNS resolving (Default: ```/etc/resolv.conf```)
`TRAEFIK_HOSTRESOLVER_RESOLVDEPTH`:
The maximal depth of DNS recursive resolving (Default: ```5```)
`TRAEFIK_LOG`:
Traefik log settings. (Default: "false")
`TRAEFIK_LOG_FILEPATH`:
Traefik log file path. Stdout is used when omitted or empty.
@ -241,16 +244,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 +367,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 +384,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 +427,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 }}`)```)

View file

@ -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"

View file

@ -44,22 +44,22 @@ You can define them using a toml file, CLI arguments, or a key-value store.
See the complete reference for the list of available options:
```toml tab="File"
[EntryPoints]
[entryPoints]
[EntryPoints.EntryPoint0]
[entryPoints.EntryPoint0]
Address = ":8888"
[EntryPoints.EntryPoint0.Transport]
[EntryPoints.EntryPoint0.Transport.LifeCycle]
[entryPoints.EntryPoint0.Transport]
[entryPoints.EntryPoint0.Transport.LifeCycle]
RequestAcceptGraceTimeout = 42
GraceTimeOut = 42
[EntryPoints.EntryPoint0.Transport.RespondingTimeouts]
[entryPoints.EntryPoint0.Transport.RespondingTimeouts]
ReadTimeout = 42
WriteTimeout = 42
IdleTimeout = 42
[EntryPoints.EntryPoint0.ProxyProtocol]
[entryPoints.EntryPoint0.ProxyProtocol]
Insecure = true
TrustedIPs = ["foobar", "foobar"]
[EntryPoints.EntryPoint0.ForwardedHeaders]
[entryPoints.EntryPoint0.ForwardedHeaders]
Insecure = true
TrustedIPs = ["foobar", "foobar"]
```

View file

@ -36,8 +36,10 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
[entryPoints.mysql-default]
address = ":80"
[entryPoints.mysql-default]
address = ":3306"
address = ":3306"
```
```toml
[tcp]
[tcp.routers]
[tcp.routers.to-database]
@ -50,8 +52,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
### EntryPoints
If not specified, HTTP routers will accept requests from all defined entrypoints.
If you want to limit the router scope to a set of entrypoints, set the entrypoints option.
If not specified, HTTP routers will accept requests from all defined entry points.
If you want to limit the router scope to a set of entry points, set the `entryPoints` option.
??? example "Listens to Every EntryPoint"
@ -63,7 +65,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
# ...
[entryPoints.other]
# ...
```
```toml
[http.routers]
[http.routers.Router-1]
# By default, routers listen to every entrypoints
@ -81,7 +85,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
# ...
[entryPoints.other]
# ...
```
```toml
[http.routers]
[http.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
@ -97,14 +103,15 @@ If the rule is verified, the router becomes active, calls middlewares, and then
??? example "Host is traefik.io"
```toml
rule = "Host(`traefik.io`)"
rule = "Host(`traefik.io`)"
```
??? example "Host is traefik.io OR Host is containo.us AND path is /traefik"
```toml
rule = "Host(`traefik.io`) || (Host(`containo.us`) && Path(`/traefik`))"
rule = "Host(`traefik.io`) || (Host(`containo.us`) && Path(`/traefik`))"
```
The table below lists all the available matchers:
| Rule | Description |
@ -126,7 +133,7 @@ The table below lists all the available matchers:
!!! tip "Combining Matchers Using Operators and Parenthesis"
You can combine multiple matchers using the AND (`&&`) and OR (`||) operators. You can also use parenthesis.
You can combine multiple matchers using the AND (`&&`) and OR (`||`) operators. You can also use parenthesis.
!!! important "Rule, Middleware, and Services"
@ -212,7 +219,6 @@ It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied
[http.routers.Router-1.tls] # will terminate the TLS request
options = "foo"
[tlsOptions]
[tlsOptions.foo]
minVersion = "VersionTLS12"
@ -234,7 +240,7 @@ If no matching route is found for the TCP routers, then the HTTP routers will ta
If not specified, TCP routers will accept requests from all defined entry points.
If you want to limit the router scope to a set of entry points, set the entry points option.
??? example "Listens to Every EntryPoint"
??? example "Listens to Every Entry Point"
```toml
[entryPoints]
@ -244,7 +250,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
# ...
[entryPoints.other]
# ...
```
```toml
[tcp.routers]
[tcp.routers.Router-1]
# By default, routers listen to every entrypoints
@ -253,7 +261,7 @@ If you want to limit the router scope to a set of entry points, set the entry po
[tcp.routers.Router-1.tls] # will route TLS requests (and ignore non tls requests)
```
??? example "Listens to Specific EntryPoints"
??? example "Listens to Specific Entry Points"
```toml
[entryPoints]
@ -263,7 +271,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
# ...
[entryPoints.other]
# ...
```
```toml
[tcp.routers]
[tcp.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
@ -340,7 +350,6 @@ It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied
[tcp.routers.Router-1.tls] # will terminate the TLS request
options = "foo"
[tlsOptions]
[tlsOptions.foo]
minVersion = "VersionTLS12"

View file

@ -16,6 +16,10 @@ In the following, the Kubernetes resources defined in YAML configuration files c
- the first, and usual way, is simply with the `kubectl apply` command.
- the second, which can be used for this tutorial, is to directly place the files in the directory used by the k3s docker image for such inputs (`/var/lib/rancher/k3s/server/manifests`).
!!! important "Kubectl Version"
With the `rancher/k3s` version used in this guide (`0.5.0`), the kubectl version needs to be >= `0.11`.
## k3s Docker-compose Configuration
Our starting point is the docker-compose configuration file, to start the k3s cluster.

View file

@ -581,7 +581,7 @@ func CheckAccessLogFormat(c *check.C, line string, i int) {
c.Assert(results, checker.HasLen, 14)
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`)
c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
c.Assert(results[accesslog.RouterName], checker.HasPrefix, "\"docker@rt-")
c.Assert(results[accesslog.RouterName], checker.Matches, `"rt-.+@docker"`)
c.Assert(results[accesslog.ServiceURL], checker.HasPrefix, "\"http://")
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
}
@ -596,7 +596,7 @@ func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue)
}
c.Assert(results[accesslog.OriginStatus], checker.Equals, v.code)
c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?(docker@)?`+v.routerName+`.*$`)
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?`+v.routerName+`.*(@docker)?$`)
c.Assert(results[accesslog.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`)
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
}

View file

@ -77,7 +77,7 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
services := rtconf.Services
c.Assert(services, checker.HasLen, 1)
for k, v := range services {
c.Assert(k, checker.Equals, "docker@"+composeService+"_integrationtest"+composeProject)
c.Assert(k, checker.Equals, composeService+"_integrationtest"+composeProject+"@docker")
c.Assert(v.LoadBalancer.Servers, checker.HasLen, serviceCount)
// We could break here, but we don't just to keep us honest.
}

View file

@ -44,7 +44,6 @@ level = "DEBUG"
[[http.services.service2.LoadBalancer.Servers]]
URL = "http://127.0.0.1:9020"
[[tls]]
[tls.certificate]
certFile = "fixtures/https/snitest.com.cert"

View file

@ -41,3 +41,18 @@ spec:
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced

View file

@ -15,3 +15,7 @@ spec:
services:
- name: whoami
port: 80
tls:
options:
name: mytlsoption

View file

@ -0,0 +1,12 @@
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: mytlsoption
namespace: default
spec:
minversion: VersionTLS12
snistrict: true
ciphersuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384

View file

@ -12,3 +12,6 @@ spec:
services:
- name: whoamitcp
port: 8080
tls:
options:
name: mytlsoption

View file

@ -13,7 +13,7 @@ level = "DEBUG"
address = ":8001"
[api]
middlewares = ["file@authentication"]
middlewares = ["authentication@file"]
[ping]

View file

@ -191,7 +191,7 @@ func (s *HTTPSSuite) TestWithTLSOptions(c *check.C) {
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
// with unknown tls option
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("unknown TLS options: unknown"))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("unknown TLS options: unknown@file"))
c.Assert(err, checker.IsNil)
}

View file

@ -444,8 +444,8 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
Routers: map[string]*config.Router{
"router1": {
EntryPoints: []string{"web"},
Middlewares: []string{"file@customheader"},
Service: "file@service",
Middlewares: []string{"customheader@file"},
Service: "service@file",
Rule: "PathPrefix(`/`)",
},
},

View file

@ -1,14 +1,17 @@
{
"routers": {
"kubernetescrd@default/test-crd-6b204d94623b3df4370c": {
"default/test-crd-6b204d94623b3df4370c@kubernetescrd": {
"entryPoints": [
"web"
],
"service": "default/test-crd-6b204d94623b3df4370c",
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/bar`)",
"priority": 12
"priority": 12,
"tls": {
"options": "default/mytlsoption"
}
},
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016": {
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd": {
"entryPoints": [
"web"
],
@ -20,82 +23,86 @@
}
},
"middlewares": {
"kubernetescrd@default/stripprefix": {
"default/stripprefix@kubernetescrd": {
"stripPrefix": {
"prefixes": [
"/tobestripped"
]
},
"usedBy": [
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016"
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd"
]
}
},
"services": {
"kubernetescrd@default/test-crd-6b204d94623b3df4370c": {
"default/test-crd-6b204d94623b3df4370c@kubernetescrd": {
"loadbalancer": {
"servers": [
{
"url": "http://10.42.0.4:80"
"url": "http://10.42.0.2:80"
},
{
"url": "http://10.42.0.5:80"
"url": "http://10.42.0.6:80"
}
],
"passHostHeader": true
},
"usedBy": [
"kubernetescrd@default/test-crd-6b204d94623b3df4370c"
"default/test-crd-6b204d94623b3df4370c@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.4:80": "UP",
"http://10.42.0.5:80": "UP"
"http://10.42.0.2:80": "UP",
"http://10.42.0.6:80": "UP"
}
},
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016": {
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd": {
"loadbalancer": {
"servers": [
{
"url": "http://10.42.0.4:80"
"url": "http://10.42.0.2:80"
},
{
"url": "http://10.42.0.5:80"
"url": "http://10.42.0.6:80"
}
],
"passHostHeader": true
},
"usedBy": [
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016"
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.4:80": "UP",
"http://10.42.0.5:80": "UP"
"http://10.42.0.2:80": "UP",
"http://10.42.0.6:80": "UP"
}
}
},
"tcpRouters": {
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a": {
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd": {
"entryPoints": [
"footcp"
],
"service": "default/test3-crd-673acf455cb2dab0b43a",
"rule": "HostSNI(`*`)"
"rule": "HostSNI(`*`)",
"tls": {
"passthrough": false,
"options": "default/mytlsoption"
}
}
},
"tcpServices": {
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a": {
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd": {
"loadbalancer": {
"servers": [
{
"address": "10.42.0.2:8080"
"address": "10.42.0.3:8080"
},
{
"address": "10.42.0.3:8080"
"address": "10.42.0.4:8080"
}
]
},
"usedBy": [
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a"
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd"
]
}
}

View file

@ -1,13 +1,13 @@
{
"routers": {
"kubernetes@whoami-test/whoami": {
"whoami-test/whoami@kubernetes": {
"entryPoints": null,
"service": "default/whoami/http",
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)"
}
},
"services": {
"kubernetes@default/whoami/http": {
"default/whoami/http@kubernetes": {
"loadbalancer": {
"servers": [
{
@ -20,7 +20,7 @@
"passHostHeader": true
},
"usedBy": [
"kubernetes@whoami-test/whoami"
"whoami-test/whoami@kubernetes"
],
"serverStatus": {
"http://10.42.0.2:80": "UP",

View file

@ -89,7 +89,7 @@ func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/file@router1", "file@ratelimit"))
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit@file"))
c.Assert(err, checker.IsNil)
}
@ -117,7 +117,7 @@ func (s *TracingSuite) TestZipkinRetry(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/file@router2", "file@retry"))
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
c.Assert(err, checker.IsNil)
}
@ -144,6 +144,6 @@ func (s *TracingSuite) TestZipkinAuth(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "file@basic-auth"))
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
}

View file

@ -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(`/`)",

View file

@ -156,6 +156,7 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
@ -180,6 +181,8 @@ func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
Provider: getProviderName(routerID),
}
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -209,6 +212,7 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
@ -234,6 +238,8 @@ func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
ServerStatus: service.GetAllStatus(),
}
rw.Header().Add("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -262,6 +268,7 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
@ -286,6 +293,8 @@ func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
Provider: getProviderName(middlewareID),
}
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -314,6 +323,7 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
@ -338,6 +348,8 @@ func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
Provider: getProviderName(routerID),
}
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -366,6 +378,7 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
@ -390,6 +403,8 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
Provider: getProviderName(serviceID),
}
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -414,6 +429,8 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
TCPServices: h.runtimeConfiguration.TCPServices,
}
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
@ -464,5 +481,5 @@ func getIntParam(request *http.Request, key string, defaultValue int) (int, erro
}
func getProviderName(id string) string {
return strings.SplitN(id, ".", 2)[0]
return strings.SplitN(id, "@", 2)[1]
}

View file

@ -47,20 +47,20 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/routers",
conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@test": {
"test@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
TLS: &config.RouterTCPTLSConfig{
Passthrough: false,
},
},
},
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -77,24 +77,24 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/routers?page=2&per_page=1",
conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
"myprovider@baz": {
"baz@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`toto.bar`)",
},
},
"myprovider@test": {
"test@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
},
},
@ -108,13 +108,13 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one TCP router by id",
path: "/api/tcp/routers/myprovider@bar",
path: "/api/tcp/routers/bar@myprovider",
conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -127,13 +127,13 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one TCP router by id, that does not exist",
path: "/api/tcp/routers/myprovider@foo",
path: "/api/tcp/routers/foo@myprovider",
conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -145,7 +145,7 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one TCP router by id, but no config",
path: "/api/tcp/routers/myprovider@bar",
path: "/api/tcp/routers/bar@myprovider",
conf: config.RuntimeConfiguration{},
expected: expected{
statusCode: http.StatusNotFound,
@ -166,7 +166,7 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/services",
conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -176,9 +176,9 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
"myprovider@baz": {
"baz@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -188,7 +188,7 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo"},
UsedBy: []string{"foo@myprovider"},
},
},
},
@ -203,7 +203,7 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/services?page=2&per_page=1",
conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -213,9 +213,9 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
"myprovider@baz": {
"baz@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -225,9 +225,9 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo"},
UsedBy: []string{"foo@myprovider"},
},
"myprovider@test": {
"test@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -248,10 +248,10 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one tcp service by id",
path: "/api/tcp/services/myprovider@bar",
path: "/api/tcp/services/bar@myprovider",
conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -261,7 +261,7 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
@ -272,10 +272,10 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one tcp service by id, that does not exist",
path: "/api/tcp/services/myprovider@nono",
path: "/api/tcp/services/nono@myprovider",
conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -285,7 +285,7 @@ func TestHandlerTCP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
@ -295,7 +295,7 @@ func TestHandlerTCP_API(t *testing.T) {
},
{
desc: "one tcp service by id, but no config",
path: "/api/tcp/services/myprovider@foo",
path: "/api/tcp/services/foo@myprovider",
conf: config.RuntimeConfiguration{},
expected: expected{
statusCode: http.StatusNotFound,
@ -326,6 +326,8 @@ func TestHandlerTCP_API(t *testing.T) {
return
}
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
@ -379,20 +381,20 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/routers",
conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@test": {
"test@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"},
},
},
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
},
@ -408,25 +410,25 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/routers?page=2&per_page=1",
conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
"myprovider@baz": {
"baz@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`toto.bar`)",
},
},
"myprovider@test": {
"test@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"},
},
@ -473,15 +475,15 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one router by id",
path: "/api/http/routers/myprovider@bar",
path: "/api/http/routers/bar@myprovider",
conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
},
@ -493,15 +495,15 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one router by id, that does not exist",
path: "/api/http/routers/myprovider@foo",
path: "/api/http/routers/foo@myprovider",
conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
},
@ -512,7 +514,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one router by id, but no config",
path: "/api/http/routers/myprovider@foo",
path: "/api/http/routers/foo@myprovider",
conf: config.RuntimeConfiguration{},
expected: expected{
statusCode: http.StatusNotFound,
@ -533,7 +535,7 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/services",
conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo {
"bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -544,12 +546,12 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateStatus("http://127.0.0.1", "UP")
return si
}(),
"myprovider@baz": func() *config.ServiceInfo {
"baz@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -560,7 +562,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo"},
UsedBy: []string{"foo@myprovider"},
}
si.UpdateStatus("http://127.0.0.2", "UP")
return si
@ -578,7 +580,7 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/services?page=2&per_page=1",
conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo {
"bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -589,12 +591,12 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateStatus("http://127.0.0.1", "UP")
return si
}(),
"myprovider@baz": func() *config.ServiceInfo {
"baz@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -605,12 +607,12 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo"},
UsedBy: []string{"foo@myprovider"},
}
si.UpdateStatus("http://127.0.0.2", "UP")
return si
}(),
"myprovider@test": func() *config.ServiceInfo {
"test@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -621,7 +623,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateStatus("http://127.0.0.4", "UP")
return si
@ -636,10 +638,10 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one service by id",
path: "/api/http/services/myprovider@bar",
path: "/api/http/services/bar@myprovider",
conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo {
"bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -650,7 +652,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateStatus("http://127.0.0.1", "UP")
return si
@ -664,10 +666,10 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one service by id, that does not exist",
path: "/api/http/services/myprovider@nono",
path: "/api/http/services/nono@myprovider",
conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo {
"bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
@ -678,7 +680,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
},
},
UsedBy: []string{"myprovider@foo", "myprovider@test"},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateStatus("http://127.0.0.1", "UP")
return si
@ -691,7 +693,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one service by id, but no config",
path: "/api/http/services/myprovider@foo",
path: "/api/http/services/foo@myprovider",
conf: config.RuntimeConfiguration{},
expected: expected{
statusCode: http.StatusNotFound,
@ -712,29 +714,29 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/middlewares",
conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
UsedBy: []string{"myprovider@bar", "myprovider@test"},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/titi",
},
},
UsedBy: []string{"myprovider@test"},
UsedBy: []string{"test@myprovider"},
},
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
},
},
UsedBy: []string{"myprovider@bar"},
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -749,29 +751,29 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/middlewares?page=2&per_page=1",
conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
UsedBy: []string{"myprovider@bar", "myprovider@test"},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/titi",
},
},
UsedBy: []string{"myprovider@test"},
UsedBy: []string{"test@myprovider"},
},
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
},
},
UsedBy: []string{"myprovider@bar"},
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -783,32 +785,32 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one middleware by id",
path: "/api/http/middlewares/myprovider@auth",
path: "/api/http/middlewares/auth@myprovider",
conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
UsedBy: []string{"myprovider@bar", "myprovider@test"},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/titi",
},
},
UsedBy: []string{"myprovider@test"},
UsedBy: []string{"test@myprovider"},
},
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
},
},
UsedBy: []string{"myprovider@bar"},
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -819,16 +821,16 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one middleware by id, that does not exist",
path: "/api/http/middlewares/myprovider@foo",
path: "/api/http/middlewares/foo@myprovider",
conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
UsedBy: []string{"myprovider@bar", "myprovider@test"},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
},
@ -838,7 +840,7 @@ func TestHandlerHTTP_API(t *testing.T) {
},
{
desc: "one middleware by id, but no config",
path: "/api/http/middlewares/myprovider@foo",
path: "/api/http/middlewares/foo@myprovider",
conf: config.RuntimeConfiguration{},
expected: expected{
statusCode: http.StatusNotFound,
@ -869,6 +871,7 @@ func TestHandlerHTTP_API(t *testing.T) {
return
}
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
@ -912,7 +915,7 @@ func TestHandler_Configuration(t *testing.T) {
path: "/api/rawdata",
conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -925,21 +928,21 @@ func TestHandler_Configuration(t *testing.T) {
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/titi",
},
},
},
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
@ -948,25 +951,25 @@ func TestHandler_Configuration(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
"myprovider@test": {
"test@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"},
},
},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@tcpfoo-service": {
"tcpfoo-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -979,23 +982,22 @@ func TestHandler_Configuration(t *testing.T) {
},
},
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@tcpbar": {
"tcpbar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@tcpfoo-service",
Service: "tcpfoo-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
},
"myprovider@tcptest": {
"tcptest@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@tcpfoo-service",
Service: "tcpfoo-service@myprovider",
Rule: "HostSNI(`foo.bar.other`)",
},
},
},
},
expected: expected{
statusCode: http.StatusOK,
json: "testdata/getrawdata.json",
@ -1011,6 +1013,7 @@ func TestHandler_Configuration(t *testing.T) {
// TODO: server status
rtConf := &test.conf
rtConf.PopulateUsedBy()
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
router := mux.NewRouter()
@ -1022,6 +1025,7 @@ func TestHandler_Configuration(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, test.expected.statusCode, resp.StatusCode)
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
@ -1054,10 +1058,10 @@ func TestHandler_Configuration(t *testing.T) {
func generateHTTPRouters(nbRouters int) map[string]*config.RouterInfo {
routers := make(map[string]*config.RouterInfo, nbRouters)
for i := 0; i < nbRouters; i++ {
routers[fmt.Sprintf("myprovider@bar%2d", i)] = &config.RouterInfo{
routers[fmt.Sprintf("bar%2d@myprovider", i)] = &config.RouterInfo{
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar" + strconv.Itoa(i) + "`)",
},
}

View file

@ -1,17 +1,17 @@
{
"routers": {
"myprovider@bar": {
"bar@myprovider": {
"entryPoints": [
"web"
],
"middlewares": [
"auth",
"anotherprovider@addPrefixTest"
"addPrefixTest@anotherprovider"
],
"service": "myprovider@foo-service",
"service": "foo-service@myprovider",
"rule": "Host(`foo.bar`)"
},
"myprovider@test": {
"test@myprovider": {
"entryPoints": [
"web"
],
@ -19,41 +19,41 @@
"addPrefixTest",
"auth"
],
"service": "myprovider@foo-service",
"service": "foo-service@myprovider",
"rule": "Host(`foo.bar.other`)"
}
},
"middlewares": {
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
"addPrefix": {
"prefix": "/toto"
},
"usedBy": [
"myprovider@bar"
"bar@myprovider"
]
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
"addPrefix": {
"prefix": "/titi"
},
"usedBy": [
"myprovider@test"
"test@myprovider"
]
},
"myprovider@auth": {
"auth@myprovider": {
"basicAuth": {
"users": [
"admin:admin"
]
},
"usedBy": [
"myprovider@bar",
"myprovider@test"
"bar@myprovider",
"test@myprovider"
]
}
},
"services": {
"myprovider@foo-service": {
"foo-service@myprovider": {
"loadbalancer": {
"servers": [
{
@ -63,29 +63,29 @@
"passHostHeader": false
},
"usedBy": [
"myprovider@bar",
"myprovider@test"
"bar@myprovider",
"test@myprovider"
]
}
},
"tcpRouters": {
"myprovider@tcpbar": {
"tcpbar@myprovider": {
"entryPoints": [
"web"
],
"service": "myprovider@tcpfoo-service",
"service": "tcpfoo-service@myprovider",
"rule": "HostSNI(`foo.bar`)"
},
"myprovider@tcptest": {
"tcptest@myprovider": {
"entryPoints": [
"web"
],
"service": "myprovider@tcpfoo-service",
"service": "tcpfoo-service@myprovider",
"rule": "HostSNI(`foo.bar.other`)"
}
},
"tcpServices": {
"myprovider@tcpfoo-service": {
"tcpfoo-service@myprovider": {
"loadbalancer": {
"servers": [
{
@ -94,8 +94,8 @@
]
},
"usedBy": [
"myprovider@tcpbar",
"myprovider@tcptest"
"tcpbar@myprovider",
"tcptest@myprovider"
]
}
}

View file

@ -4,10 +4,10 @@
"admin:admin"
]
},
"name": "myprovider@auth",
"provider": "myprovider@auth",
"name": "auth@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@bar",
"myprovider@test"
"bar@myprovider",
"test@myprovider"
]
}

View file

@ -3,10 +3,10 @@
"addPrefix": {
"prefix": "/titi"
},
"name": "myprovider@addPrefixTest",
"provider": "myprovider@addPrefixTest",
"name": "addPrefixTest@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@test"
"test@myprovider"
]
}
]

View file

@ -3,20 +3,20 @@
"addPrefix": {
"prefix": "/toto"
},
"name": "anotherprovider@addPrefixTest",
"provider": "anotherprovider@addPrefixTest",
"name": "addPrefixTest@anotherprovider",
"provider": "anotherprovider",
"usedBy": [
"myprovider@bar"
"bar@myprovider"
]
},
{
"addPrefix": {
"prefix": "/titi"
},
"name": "myprovider@addPrefixTest",
"provider": "myprovider@addPrefixTest",
"name": "addPrefixTest@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@test"
"test@myprovider"
]
},
{
@ -25,11 +25,11 @@
"admin:admin"
]
},
"name": "myprovider@auth",
"provider": "myprovider@auth",
"name": "auth@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@bar",
"myprovider@test"
"bar@myprovider",
"test@myprovider"
]
}
]

View file

@ -4,10 +4,10 @@
],
"middlewares": [
"auth",
"anotherprovider@addPrefixTest"
"addPrefixTest@anotherprovider"
],
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}

View file

@ -3,45 +3,45 @@
"entryPoints": [
"web"
],
"name": "myprovider@bar14",
"provider": "myprovider@bar14",
"name": "bar14@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar14`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"name": "myprovider@bar15",
"provider": "myprovider@bar15",
"name": "bar15@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar15`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"name": "myprovider@bar16",
"provider": "myprovider@bar16",
"name": "bar16@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar16`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"name": "myprovider@bar17",
"provider": "myprovider@bar17",
"name": "bar17@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar17`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"name": "myprovider@bar18",
"provider": "myprovider@bar18",
"name": "bar18@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar18`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}
]

View file

@ -3,9 +3,9 @@
"entryPoints": [
"web"
],
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"rule": "Host(`toto.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}
]

View file

@ -5,12 +5,12 @@
],
"middlewares": [
"auth",
"anotherprovider@addPrefixTest"
"addPrefixTest@anotherprovider"
],
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
@ -20,9 +20,9 @@
"addPrefixTest",
"auth"
],
"name": "myprovider@test",
"provider": "myprovider@test",
"name": "test@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar.other`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}
]

View file

@ -7,13 +7,13 @@
}
]
},
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"usedBy": [
"myprovider@foo",
"myprovider@test"
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -8,13 +8,13 @@
}
]
},
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.2": "UP"
},
"usedBy": [
"myprovider@foo"
"foo@myprovider"
]
}
]

View file

@ -8,14 +8,14 @@
}
]
},
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"usedBy": [
"myprovider@foo",
"myprovider@test"
"foo@myprovider",
"test@myprovider"
]
},
{
@ -27,13 +27,13 @@
}
]
},
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.2": "UP"
},
"usedBy": [
"myprovider@foo"
"foo@myprovider"
]
}
]

View file

@ -2,8 +2,8 @@
"entryPoints": [
"web"
],
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}

View file

@ -3,9 +3,9 @@
"entryPoints": [
"web"
],
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"rule": "Host(`toto.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
}
]

View file

@ -3,19 +3,19 @@
"entryPoints": [
"web"
],
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "myprovider@foo-service"
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"name": "myprovider@test",
"provider": "myprovider@test",
"name": "test@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar.other`)",
"service": "myprovider@foo-service",
"service": "foo-service@myprovider",
"tls": {
"passthrough": false
}

View file

@ -6,10 +6,10 @@
}
]
},
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@foo",
"myprovider@test"
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -7,10 +7,10 @@
}
]
},
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@foo"
"foo@myprovider"
]
}
]

View file

@ -7,11 +7,11 @@
}
]
},
"name": "myprovider@bar",
"provider": "myprovider@bar",
"name": "bar@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@foo",
"myprovider@test"
"foo@myprovider",
"test@myprovider"
]
},
{
@ -22,10 +22,10 @@
}
]
},
"name": "myprovider@baz",
"provider": "myprovider@baz",
"name": "baz@myprovider",
"provider": "myprovider",
"usedBy": [
"myprovider@foo"
"foo@myprovider"
]
}
]

View file

@ -128,5 +128,5 @@ func contains(cmds []*Command, name string) bool {
}
func isFlag(arg string) bool {
return len(arg) > 0 && arg[1] == '-'
return len(arg) > 0 && arg[0] == '-'
}

View file

@ -14,24 +14,14 @@ type EnvLoader struct{}
// Load loads the command's configuration from the environment variables.
func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
return e.load(os.Environ(), cmd)
}
func (*EnvLoader) load(environ []string, cmd *Command) (bool, error) {
var found bool
for _, value := range environ {
if strings.HasPrefix(value, "TRAEFIK_") {
found = true
break
}
}
if !found {
vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, cmd.Configuration)
if len(vars) == 0 {
return false, nil
}
if err := env.Decode(environ, cmd.Configuration); err != nil {
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err)
if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
log.WithoutContext().Debug("environment variables", strings.Join(vars, ", "))
return false, fmt.Errorf("failed to decode configuration from environment variables: %v ", err)
}
log.WithoutContext().Println("Configuration loaded from environment variables.")

View file

@ -12,6 +12,10 @@ type FlagLoader struct{}
// Load loads the command's configuration from flag arguments.
func (*FlagLoader) Load(args []string, cmd *Command) (bool, error) {
if len(args) == 0 {
return false, nil
}
if err := flag.Decode(args, cmd.Configuration); err != nil {
return false, fmt.Errorf("failed to decode configuration from flags: %v", err)
}

32
pkg/config/env/env.go vendored
View file

@ -2,28 +2,38 @@
package env
import (
"fmt"
"regexp"
"strings"
"github.com/containous/traefik/pkg/config/parser"
)
// DefaultNamePrefix is the default prefix for environment variable names.
const DefaultNamePrefix = "TRAEFIK_"
// Decode decodes the given environment variables into the given element.
// The operation goes through four stages roughly summarized as:
// env vars -> map
// map -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element
func Decode(environ []string, element interface{}) error {
func Decode(environ []string, prefix string, element interface{}) error {
if err := checkPrefix(prefix); err != nil {
return err
}
vars := make(map[string]string)
for _, evr := range environ {
n := strings.SplitN(evr, "=", 2)
if strings.HasPrefix(strings.ToUpper(n[0]), "TRAEFIK_") {
if strings.HasPrefix(strings.ToUpper(n[0]), prefix) {
key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
vars[key] = n[1]
}
}
return parser.Decode(vars, element)
rootName := strings.ToLower(prefix[:len(prefix)-1])
return parser.Decode(vars, element, rootName)
}
// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
@ -36,7 +46,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
return nil, nil
}
node, err := parser.EncodeToNode(element, false)
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
if err != nil {
return nil, err
}
@ -48,3 +58,17 @@ func Encode(element interface{}) ([]parser.Flat, error) {
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"})
}
func checkPrefix(prefix string) error {
prefixPattern := `[a-zA-Z0-9]+_`
matched, err := regexp.MatchString(prefixPattern, prefix)
if err != nil {
return err
}
if !matched {
return fmt.Errorf("invalid prefix %q, the prefix pattern must match the following pattern: %s", prefix, prefixPattern)
}
return nil
}

View file

@ -173,7 +173,7 @@ func TestDecode(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := Decode(test.environ, test.element)
err := Decode(test.environ, DefaultNamePrefix, test.element)
require.NoError(t, err)
assert.Equal(t, test.expected, test.element)
@ -460,39 +460,3 @@ func TestEncode(t *testing.T) {
assert.Equal(t, expected, flats)
}
type Ya struct {
Foo *Yaa
Field1 string
Field2 bool
Field3 int
Field4 map[string]string
Field5 map[string]int
Field6 map[string]struct{ Field string }
Field7 map[string]struct{ Field map[string]string }
Field8 map[string]*struct{ Field string }
Field9 map[string]*struct{ Field map[string]string }
Field10 struct{ Field string }
Field11 *struct{ Field string }
Field12 *string
Field13 *bool
Field14 *int
Field15 []int
}
type Yaa struct {
FieldIn1 string
FieldIn2 bool
FieldIn3 int
FieldIn4 map[string]string
FieldIn5 map[string]int
FieldIn6 map[string]struct{ Field string }
FieldIn7 map[string]struct{ Field map[string]string }
FieldIn8 map[string]*struct{ Field string }
FieldIn9 map[string]*struct{ Field map[string]string }
FieldIn10 struct{ Field string }
FieldIn11 *struct{ Field string }
FieldIn12 *string
FieldIn13 *bool
FieldIn14 *int
}

64
pkg/config/env/filter.go vendored Normal file
View file

@ -0,0 +1,64 @@
package env
import (
"reflect"
"strings"
"github.com/containous/traefik/pkg/config/parser"
)
// FindPrefixedEnvVars finds prefixed environment variables.
func FindPrefixedEnvVars(environ []string, prefix string, element interface{}) []string {
prefixes := getRootPrefixes(element, prefix)
var values []string
for _, px := range prefixes {
for _, value := range environ {
if strings.HasPrefix(value, px) {
values = append(values, value)
}
}
}
return values
}
func getRootPrefixes(element interface{}, prefix string) []string {
if element == nil {
return nil
}
rootType := reflect.TypeOf(element)
return getPrefixes(prefix, rootType)
}
func getPrefixes(prefix string, rootType reflect.Type) []string {
var names []string
if rootType.Kind() == reflect.Ptr {
rootType = rootType.Elem()
}
if rootType.Kind() != reflect.Struct {
return nil
}
for i := 0; i < rootType.NumField(); i++ {
field := rootType.Field(i)
if !parser.IsExported(field) {
continue
}
if field.Anonymous &&
(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct || field.Type.Kind() == reflect.Struct) {
names = append(names, getPrefixes(prefix, field.Type)...)
continue
}
names = append(names, prefix+strings.ToUpper(field.Name))
}
return names
}

87
pkg/config/env/filter_test.go vendored Normal file
View file

@ -0,0 +1,87 @@
package env
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFindPrefixedEnvVars(t *testing.T) {
testCases := []struct {
desc string
environ []string
element interface{}
expected []string
}{
{
desc: "exact name",
environ: []string{"TRAEFIK_FOO"},
element: &Yo{},
expected: []string{"TRAEFIK_FOO"},
},
{
desc: "prefixed name",
environ: []string{"TRAEFIK_FII01"},
element: &Yo{},
expected: []string{"TRAEFIK_FII01"},
},
{
desc: "excluded env vars",
environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO"},
element: &Yo{},
expected: nil,
},
{
desc: "filter",
environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO", "TRAEFIK_FOO", "TRAEFIK_FII01"},
element: &Yo{},
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII01"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
vars := FindPrefixedEnvVars(test.environ, DefaultNamePrefix, test.element)
assert.Equal(t, test.expected, vars)
})
}
}
func Test_getRootFieldNames(t *testing.T) {
testCases := []struct {
desc string
element interface{}
expected []string
}{
{
desc: "simple fields",
element: &Yo{},
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU", "TRAEFIK_YI", "TRAEFIK_YU"},
},
{
desc: "embedded struct",
element: &Yu{},
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
},
{
desc: "embedded struct pointer",
element: &Ye{},
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
names := getRootPrefixes(test.element, DefaultNamePrefix)
assert.Equal(t, test.expected, names)
})
}
}

69
pkg/config/env/fixtures_test.go vendored Normal file
View file

@ -0,0 +1,69 @@
package env
type Ya struct {
Foo *Yaa
Field1 string
Field2 bool
Field3 int
Field4 map[string]string
Field5 map[string]int
Field6 map[string]struct{ Field string }
Field7 map[string]struct{ Field map[string]string }
Field8 map[string]*struct{ Field string }
Field9 map[string]*struct{ Field map[string]string }
Field10 struct{ Field string }
Field11 *struct{ Field string }
Field12 *string
Field13 *bool
Field14 *int
Field15 []int
}
type Yaa struct {
FieldIn1 string
FieldIn2 bool
FieldIn3 int
FieldIn4 map[string]string
FieldIn5 map[string]int
FieldIn6 map[string]struct{ Field string }
FieldIn7 map[string]struct{ Field map[string]string }
FieldIn8 map[string]*struct{ Field string }
FieldIn9 map[string]*struct{ Field map[string]string }
FieldIn10 struct{ Field string }
FieldIn11 *struct{ Field string }
FieldIn12 *string
FieldIn13 *bool
FieldIn14 *int
}
type Yo struct {
Foo string `description:"Foo description"`
Fii string `description:"Fii description"`
Fuu string `description:"Fuu description"`
Yi *Yi `label:"allowEmpty"`
Yu *Yi
}
func (y *Yo) SetDefaults() {
y.Foo = "foo"
y.Fii = "fii"
}
type Yi struct {
Foo string
Fii string
Fuu string
}
func (y *Yi) SetDefaults() {
y.Foo = "foo"
y.Fii = "fii"
}
type Yu struct {
Yi
}
type Ye struct {
*Yi
}

View file

@ -36,13 +36,13 @@ func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error)
return nil, err
}
return decodeRawToNode(data, filters...)
return decodeRawToNode(data, parser.DefaultRootName, filters...)
default:
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
}
return decodeRawToNode(data, filters...)
return decodeRawToNode(data, parser.DefaultRootName, filters...)
}
func getRootFieldNames(element interface{}) []string {

View file

@ -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"},

View file

@ -74,7 +74,6 @@
DefaultRule = "foobar"
ExposedByDefault = true
DCOSToken = "foobar"
FilterMarathonConstraints = true
DialerTimeout = 42
ResponseHeaderTimeout = 42
TLSHandshakeTimeout = 42

View file

@ -69,7 +69,6 @@ Providers:
DefaultRule: foobar
ExposedByDefault: true
DCOSToken: foobar
FilterMarathonConstraints: true
DialerTimeout: 42
ResponseHeaderTimeout: 42
TLSHandshakeTimeout: 42

View file

@ -9,9 +9,9 @@ import (
"github.com/containous/traefik/pkg/config/parser"
)
func decodeRawToNode(data map[string]interface{}, filters ...string) (*parser.Node, error) {
func decodeRawToNode(data map[string]interface{}, rootName string, filters ...string) (*parser.Node, error) {
root := &parser.Node{
Name: "traefik",
Name: rootName,
}
vData := reflect.ValueOf(data)

View file

@ -531,7 +531,7 @@ func Test_decodeRawToNode(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
node, err := decodeRawToNode(test.data)
node, err := decodeRawToNode(test.data, parser.DefaultRootName)
require.NoError(t, err)
assert.Equal(t, test.expected, node)

View file

@ -17,7 +17,7 @@ func Decode(args []string, element interface{}) error {
return err
}
return parser.Decode(ref, element)
return parser.Decode(ref, element, parser.DefaultRootName)
}
// Encode encodes the configuration in element into the flags represented in the returned Flats.
@ -30,7 +30,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
return nil, nil
}
node, err := parser.EncodeToNode(element, false)
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
if err != nil {
return nil, err
}

View file

@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"strings"
"github.com/containous/traefik/pkg/config/parser"
)
// Parse parses the command-line flag arguments into a map,
@ -96,7 +98,7 @@ func (f *flagSet) parseOne() (bool, error) {
}
func (f *flagSet) setValue(name string, value string) {
n := strings.ToLower("traefik." + name)
n := strings.ToLower(parser.DefaultRootName + "." + name)
v, ok := f.values[n]
if ok && f.flagTypes[name] == reflect.Slice {

View file

@ -13,7 +13,7 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
TCP: &config.TCPConfiguration{},
}
err := parser.Decode(labels, conf, "traefik.http", "traefik.tcp")
err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp")
if err != nil {
return nil, err
}
@ -23,11 +23,11 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
// EncodeConfiguration converts a configuration to labels.
func EncodeConfiguration(conf *config.Configuration) (map[string]string, error) {
return parser.Encode(conf)
return parser.Encode(conf, parser.DefaultRootName)
}
// Decode converts the labels to an element.
// labels -> [ node -> node + metadata (type) ] -> element (node)
func Decode(labels map[string]string, element interface{}, filters ...string) error {
return parser.Decode(labels, element, filters...)
return parser.Decode(labels, element, parser.DefaultRootName, filters...)
}

View file

@ -9,9 +9,9 @@ import (
// EncodeToNode converts an element to a node.
// element -> nodes
func EncodeToNode(element interface{}, omitEmpty bool) (*Node, error) {
func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node, error) {
rValue := reflect.ValueOf(element)
node := &Node{Name: "traefik"}
node := &Node{Name: rootName}
encoder := encoderToNode{omitEmpty: omitEmpty}

View file

@ -723,7 +723,7 @@ func TestEncodeToNode(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
node, err := EncodeToNode(test.element, true)
node, err := EncodeToNode(test.element, DefaultRootName, true)
if test.expected.error {
require.Error(t, err)

View file

@ -6,18 +6,16 @@ import (
"strings"
)
const labelRoot = "traefik"
// DecodeToNode converts the labels to a tree of nodes.
// If any filters are present, labels which do not match the filters are skipped.
func DecodeToNode(labels map[string]string, filters ...string) (*Node, error) {
func DecodeToNode(labels map[string]string, rootName string, filters ...string) (*Node, error) {
sortedKeys := sortKeys(labels, filters)
var node *Node
for i, key := range sortedKeys {
split := strings.Split(key, ".")
if split[0] != labelRoot {
if split[0] != rootName {
return nil, fmt.Errorf("invalid label root %s", split[0])
}

View file

@ -218,7 +218,7 @@ func TestDecodeToNode(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
out, err := DecodeToNode(test.in, test.filters...)
out, err := DecodeToNode(test.in, DefaultRootName, test.filters...)
if test.expected.error {
require.Error(t, err)

View file

@ -2,6 +2,9 @@ package parser
import "reflect"
// DefaultRootName is the default name of the root node and the prefix of element name from the resources.
const DefaultRootName = "traefik"
// MapNamePlaceholder is the placeholder for the map name.
const MapNamePlaceholder = "<name>"

View file

@ -7,8 +7,8 @@ package parser
// labels -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element
func Decode(labels map[string]string, element interface{}, filters ...string) error {
node, err := DecodeToNode(labels, filters...)
func Decode(labels map[string]string, element interface{}, rootName string, filters ...string) error {
node, err := DecodeToNode(labels, rootName, filters...)
if err != nil {
return err
}
@ -28,8 +28,8 @@ func Decode(labels map[string]string, element interface{}, filters ...string) er
// Encode converts an element to labels.
// element -> node (value) -> label (node)
func Encode(element interface{}) (map[string]string, error) {
node, err := EncodeToNode(element, true)
func Encode(element interface{}, rootName string) (map[string]string, error) {
node, err := EncodeToNode(element, rootName, true)
if err != nil {
return nil, err
}

View file

@ -265,7 +265,7 @@ type TCPServiceInfo struct {
func getProviderName(elementName string) string {
parts := strings.Split(elementName, "@")
if len(parts) > 1 {
return parts[0]
return parts[1]
}
return ""
}
@ -273,7 +273,7 @@ func getProviderName(elementName string) string {
func getQualifiedName(provider, elementName string) string {
parts := strings.Split(elementName, "@")
if len(parts) == 1 {
return provider + "@" + elementName
return elementName + "@" + provider
}
return elementName
}

View file

@ -25,23 +25,23 @@ func TestPopulateUsedby(t *testing.T) {
desc: "One service used by two routers",
conf: &config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@foo": {
"foo@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -59,12 +59,12 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@foo": {},
"myprovider@bar": {},
"foo@myprovider": {},
"bar@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
},
},
},
@ -73,7 +73,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "One service used by two routers, but one router with wrong rule",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -84,17 +84,17 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@foo": {
"foo@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "WrongRule(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -102,12 +102,12 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@foo": {},
"myprovider@bar": {},
"foo@myprovider": {},
"bar@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
},
},
},
@ -116,17 +116,17 @@ func TestPopulateUsedby(t *testing.T) {
desc: "Broken Service used by one Router",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: nil,
},
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -134,11 +134,11 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {},
"bar@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -147,7 +147,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "2 different Services each used by a disctinct router.",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -165,7 +165,7 @@ func TestPopulateUsedby(t *testing.T) {
},
},
},
"myprovider@bar-service": {
"bar-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -185,17 +185,17 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@foo": {
"foo@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -203,15 +203,15 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {},
"myprovider@foo": {},
"bar@myprovider": {},
"foo@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"foo@myprovider"},
},
"myprovider@bar-service": {
UsedBy: []string{"myprovider@bar"},
"bar-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -220,7 +220,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "2 middlewares both used by 2 Routers",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -233,14 +233,14 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
@ -249,18 +249,18 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "addPrefixTest"},
},
},
"myprovider@test": {
"test@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"},
},
@ -269,20 +269,20 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {},
"myprovider@test": {},
"bar@myprovider": {},
"test@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@test"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
UsedBy: []string{"myprovider@bar", "myprovider@test"},
"auth@myprovider": {
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"myprovider@addPrefixTest": {
UsedBy: []string{"myprovider@bar", "myprovider@test"},
"addPrefixTest@myprovider": {
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
},
@ -291,7 +291,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "Unknown middleware is not used by the Router",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -304,7 +304,7 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
@ -313,10 +313,10 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"unknown"},
},
@ -325,8 +325,8 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -335,7 +335,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "Broken middleware is used by Router",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -348,7 +348,7 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"badConf"},
@ -357,28 +357,28 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"myprovider@auth"},
Middlewares: []string{"auth@myprovider"},
},
},
},
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {},
"bar@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
UsedBy: []string{"myprovider@bar"},
"auth@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -387,7 +387,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "2 middlewares from 2 disctinct providers both used by 2 Routers",
conf: &config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{
@ -400,21 +400,21 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
"auth@myprovider": {
Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"},
},
},
},
"myprovider@addPrefixTest": {
"addPrefixTest@myprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/titi",
},
},
},
"anotherprovider@addPrefixTest": {
"addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{
Prefix: "/toto",
@ -423,18 +423,18 @@ func TestPopulateUsedby(t *testing.T) {
},
},
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {
"bar@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
},
"myprovider@test": {
"test@myprovider": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"},
},
@ -443,23 +443,23 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{
"myprovider@bar": {},
"myprovider@test": {},
"bar@myprovider": {},
"test@myprovider": {},
},
Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@test"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": {
UsedBy: []string{"myprovider@bar", "myprovider@test"},
"auth@myprovider": {
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"myprovider@addPrefixTest": {
UsedBy: []string{"myprovider@test"},
"addPrefixTest@myprovider": {
UsedBy: []string{"test@myprovider"},
},
"anotherprovider@addPrefixTest": {
UsedBy: []string{"myprovider@bar"},
"addPrefixTest@anotherprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -470,23 +470,23 @@ func TestPopulateUsedby(t *testing.T) {
desc: "TCP, One service used by two routers",
conf: &config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@foo": {
"foo@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -506,12 +506,12 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@foo": {},
"myprovider@bar": {},
"foo@myprovider": {},
"bar@myprovider": {},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
},
},
},
@ -520,7 +520,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "TCP, One service used by two routers, but one router with wrong rule",
conf: &config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -533,17 +533,17 @@ func TestPopulateUsedby(t *testing.T) {
},
},
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@foo": {
"foo@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "WrongRule(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -551,12 +551,12 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@foo": {},
"myprovider@bar": {},
"foo@myprovider": {},
"bar@myprovider": {},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
},
},
},
@ -565,17 +565,17 @@ func TestPopulateUsedby(t *testing.T) {
desc: "TCP, Broken Service used by one Router",
conf: &config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: nil,
},
},
},
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -583,11 +583,11 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {},
"bar@myprovider": {},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@bar"},
"foo-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -596,7 +596,7 @@ func TestPopulateUsedby(t *testing.T) {
desc: "TCP, 2 different Services each used by a disctinct router.",
conf: &config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
"foo-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -612,7 +612,7 @@ func TestPopulateUsedby(t *testing.T) {
},
},
},
"myprovider@bar-service": {
"bar-service@myprovider": {
TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{
@ -630,17 +630,17 @@ func TestPopulateUsedby(t *testing.T) {
},
},
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@foo": {
"foo@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"myprovider@bar": {
"bar@myprovider": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
@ -648,15 +648,15 @@ func TestPopulateUsedby(t *testing.T) {
},
expected: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": {},
"myprovider@foo": {},
"bar@myprovider": {},
"foo@myprovider": {},
},
TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@foo-service": {
UsedBy: []string{"myprovider@foo"},
"foo-service@myprovider": {
UsedBy: []string{"foo@myprovider"},
},
"myprovider@bar-service": {
UsedBy: []string{"myprovider@bar"},
"bar-service@myprovider": {
UsedBy: []string{"bar@myprovider"},
},
},
},
@ -716,7 +716,7 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
@ -725,7 +725,7 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
},
@ -741,17 +741,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -760,17 +760,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -782,14 +782,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
"foo": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
},
"foobar": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -803,17 +803,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -822,17 +822,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -844,14 +844,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
"foo": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
},
"foobar": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -861,14 +861,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
},
"foobar": {
TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -914,7 +914,7 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
@ -923,7 +923,7 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
},
@ -939,17 +939,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -958,17 +958,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -980,14 +980,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
"foo": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"foobar": {
Router: &config.Router{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -1001,17 +1001,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -1020,17 +1020,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Routers: map[string]*config.TCPRouter{
"foo": {
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "HostSNI(`bar.foo`)",
},
"bar": {
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "HostSNI(`foo.bar`)",
},
"foobar": {
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "HostSNI(`bar.foobar`)",
},
},
@ -1042,14 +1042,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
"foo": {
Router: &config.Router{
EntryPoints: []string{"web"},
Service: "myprovider@foo-service",
Service: "foo-service@myprovider",
Rule: "Host(`bar.foo`)",
},
},
"foobar": {
Router: &config.Router{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},
@ -1059,14 +1059,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
Router: &config.Router{
EntryPoints: []string{"webs"},
Service: "myprovider@bar-service",
Service: "bar-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
"foobar": {
Router: &config.Router{
EntryPoints: []string{"web", "webs"},
Service: "myprovider@foobar-service",
Service: "foobar-service@myprovider",
Rule: "Host(`bar.foobar`)",
},
},

View file

@ -55,7 +55,7 @@ type Configuration struct {
Ping *ping.Handler `description:"Enable ping." export:"true" label:"allowEmpty"`
// Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
Log *types.TraefikLog `description:"Traefik log settings." export:"true"`
Log *types.TraefikLog `description:"Traefik log settings." export:"true" label:"allowEmpty"`
AccessLog *types.AccessLog `description:"Access log settings." export:"true" label:"allowEmpty"`
Tracing *Tracing `description:"OpenTracing configuration." export:"true" label:"allowEmpty"`

View file

@ -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
}

View 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)
}
}

View 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)
})
}
}

View file

@ -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
}

View file

@ -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{},

View file

@ -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."`

View file

@ -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
}

View file

@ -49,6 +49,7 @@ type Client interface {
GetIngressRoutes() []*v1alpha1.IngressRoute
GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP
GetMiddlewares() []*v1alpha1.Middleware
GetTLSOptions() []*v1alpha1.TLSOption
GetIngresses() []*extensionsv1beta1.Ingress
GetService(namespace, name string) (*corev1.Service, bool, error)
@ -158,6 +159,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TLSOptions().Informer().AddEventHandler(eventHandler)
factoryKube := informers.NewFilteredSharedInformerFactory(c.csKube, resyncPeriod, ns, nil)
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
@ -241,6 +243,21 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
return result
}
// GetTLSOptions
func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
var result []*v1alpha1.TLSOption
for ns, factory := range c.factoriesCrd {
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(c.labelSelector)
if err != nil {
log.Errorf("Failed to list tls options in namespace %s: %s", ns, err)
}
result = append(result, options...)
}
return result
}
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress {
var result []*extensionsv1beta1.Ingress

View file

@ -37,6 +37,7 @@ type clientMock struct {
ingressRoutes []*v1alpha1.IngressRoute
ingressRouteTCPs []*v1alpha1.IngressRouteTCP
middlewares []*v1alpha1.Middleware
tlsOptions []*v1alpha1.TLSOption
watchChan chan interface{}
}
@ -63,6 +64,8 @@ func newClientMock(paths ...string) clientMock {
c.ingressRouteTCPs = append(c.ingressRouteTCPs, o)
case *v1alpha1.Middleware:
c.middlewares = append(c.middlewares, o)
case *v1alpha1.TLSOption:
c.tlsOptions = append(c.tlsOptions, o)
case *v1beta12.Ingress:
c.ingresses = append(c.ingresses, o)
case *corev1.Secret:
@ -88,6 +91,20 @@ func (c clientMock) GetMiddlewares() []*v1alpha1.Middleware {
return c.middlewares
}
func (c clientMock) GetTLSOptions() []*v1alpha1.TLSOption {
return c.tlsOptions
}
func (c clientMock) GetTLSOption(namespace, name string) (*v1alpha1.TLSOption, bool, error) {
for _, option := range c.tlsOptions {
if option.Namespace == namespace && option.Name == name {
return option, true, nil
}
}
return nil, false, nil
}
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
return c.ingresses
}

View file

@ -0,0 +1,70 @@
apiVersion: v1
kind: Secret
metadata:
name: secretCA1
namespace: default
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: v1
kind: Secret
metadata:
name: secretCA2
namespace: default
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: foo
namespace: default
spec:
minversion: VersionTLS12
snistrict: true
ciphersuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384
clientca:
secretnames:
- secretCA1
- secretUnknown
- emptySecret
optional: true
---
apiVersion: v1
kind: Secret
metadata:
name: supersecret
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.crd
namespace: default
spec:
entryPoints:
- foo
routes:
- match: HostSNI(`foo.com`)
services:
- name: whoamitcp
port: 8000
tls:
options:
name: foo

View file

@ -0,0 +1,69 @@
apiVersion: v1
kind: Secret
metadata:
name: secretCA1
namespace: default
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: v1
kind: Secret
metadata:
name: secretCA2
namespace: default
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: foo
namespace: default
spec:
minversion: VersionTLS12
snistrict: true
ciphersuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384
clientca:
secretnames:
- secretCA1
- secretCA2
optional: true
---
apiVersion: v1
kind: Secret
metadata:
name: supersecret
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.crd
namespace: default
spec:
entryPoints:
- foo
routes:
- match: HostSNI(`foo.com`)
services:
- name: whoamitcp
port: 8000
tls:
options:
name: foo

View file

@ -0,0 +1,70 @@
apiVersion: v1
kind: Secret
metadata:
name: secretCA1
namespace: myns
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: v1
kind: Secret
metadata:
name: secretCA2
namespace: myns
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: foo
namespace: myns
spec:
minversion: VersionTLS12
snistrict: true
ciphersuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384
clientca:
secretnames:
- secretCA1
- secretCA2
optional: true
---
apiVersion: v1
kind: Secret
metadata:
name: supersecret
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.crd
namespace: default
spec:
entryPoints:
- foo
routes:
- match: HostSNI(`foo.com`)
services:
- name: whoamitcp
port: 8000
tls:
options:
name: foo
namespace: myns

View file

@ -0,0 +1,30 @@
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
name: foo
namespace: default
spec:
minversion: VersionTLS12
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.crd
namespace: default
spec:
entryPoints:
- foo
routes:
- match: HostSNI(`foo.com`)
services:
- name: whoamitcp
port: 8000
tls:
options:
name: unknown

Some files were not shown because too many files have changed in this diff Show more