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: 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 - 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: 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 - 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: 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 - 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 # 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) ## [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) [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" pruneopts = "NUT"
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c" revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
[[projects]]
branch = "master"
digest = "1:09d61699d553a4e6ec998ad29816177b1f3d3ed0c18fe923d2c174ec065c99c8"
name = "github.com/ryanuber/go-glob"
packages = ["."]
pruneopts = "NUT"
revision = "256dc444b735e061061cf46c809487313d5b0065"
[[projects]] [[projects]]
digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a" digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a"
name = "github.com/sacloud/libsacloud" name = "github.com/sacloud/libsacloud"
@ -1598,12 +1590,12 @@
revision = "3d629cff40b7040e0519628e7774ed11a95d9aff" revision = "3d629cff40b7040e0519628e7774ed11a95d9aff"
[[projects]] [[projects]]
digest = "1:ca6bac407fedc14fbeeba861dd33a821ba3a1624c10126ec6003b0a28d4139c5" digest = "1:b9d8cc221fb40078c7eb78d73b1702b5b548511b3d62bbd56b2f8180089c79af"
name = "github.com/vulcand/predicate" name = "github.com/vulcand/predicate"
packages = ["."] packages = ["."]
pruneopts = "NUT" pruneopts = "NUT"
revision = "939c094524d124c55fa8afe0e077701db4a865e2" revision = "8fbfb3ab0e94276b6b58bec378600829adc7a203"
version = "v1.0.0" version = "v1.1.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -2289,7 +2281,6 @@
"github.com/prometheus/client_golang/prometheus/promhttp", "github.com/prometheus/client_golang/prometheus/promhttp",
"github.com/prometheus/client_model/go", "github.com/prometheus/client_model/go",
"github.com/rancher/go-rancher-metadata/metadata", "github.com/rancher/go-rancher-metadata/metadata",
"github.com/ryanuber/go-glob",
"github.com/sirupsen/logrus", "github.com/sirupsen/logrus",
"github.com/stretchr/testify/assert", "github.com/stretchr/testify/assert",
"github.com/stretchr/testify/mock", "github.com/stretchr/testify/mock",

View file

@ -146,10 +146,6 @@ required = [
name = "github.com/rancher/go-rancher-metadata" name = "github.com/rancher/go-rancher-metadata"
source = "github.com/containous/go-rancher-metadata" source = "github.com/containous/go-rancher-metadata"
[[constraint]]
branch = "master"
name = "github.com/ryanuber/go-glob"
[[constraint]] [[constraint]]
name = "github.com/Masterminds/sprig" name = "github.com/Masterminds/sprig"
version = "2.19.0" version = "2.19.0"

View file

@ -8,7 +8,7 @@
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](http://goreportcard.com/report/containous/traefik) [![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) [![](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) [![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) [![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 ## Support
To get community support, you can: 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) - 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>. 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 // traefik config inits
tConfig := cmd.NewTraefikConfiguration() tConfig := cmd.NewTraefikConfiguration()
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}} loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
cmdTraefik := &cli.Command{ cmdTraefik := &cli.Command{
Name: "traefik", Name: "traefik",

View file

@ -28,7 +28,7 @@
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible: * Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
* During the Contributions Daily Meeting * During the Contributions Daily Meeting
* By an assigned maintainer * 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: ## 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" !!! 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. 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: 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) - [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
## Issue Title ## 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 fully dynamic routing configuration (referred to as the _dynamic configuration_)
- The startup configuration (referred to as the _static configuration_) - The startup configuration (referred to as the _static configuration_)
Elements in the _static configuration_ set up connections to [providers](../../providers/overview/) and define the [entrypoints](../../routing/entrypoints/) Traefik will listen to (these elements don't change often). Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
The _dynamic configuration_ contains everything that defines how the requests are handled by your system. The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
This configuration can change and is seamlessly hot-reloaded, without any request interuption or connection loss. This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
!!! warning "Incompatible Configuration" !!! warning "Incompatible Configuration"
Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now. Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now.
@ -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: There are three different, mutually exclusive, ways to define static configuration options in Traefik:
- In a configuration file - In a configuration file
- As environment variables
- In the command-line arguments - In the command-line arguments
- As environment variables
These ways are evaluated in the order listed above. 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 # 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 ## Available Configuration Options
All the configuration options are documented in their related section. 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" ```yaml tab="Docker"
# As a Docker Label # As a Docker Label
whoami: 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: labels:
# Create a middleware named `foo-add-prefix`
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo" - "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" ```yaml tab="Kubernetes"
@ -61,14 +65,44 @@ spec:
```json tab="Marathon" ```json tab="Marathon"
"labels": { "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" ```yaml tab="Rancher"
# As a Rancher Label # As a Rancher Label
labels: labels:
# Create a middleware named `foo-add-prefix`
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo" - "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" ```toml tab="File"
@ -83,25 +117,30 @@ labels:
Rule = "Host(`example.com`)" Rule = "Host(`example.com`)"
[http.middlewares] [http.middlewares]
[http.middlewares.foo-add-prefix.AddPrefix] [http.middlewares.foo-add-prefix.AddPrefix]
prefix = "/foo" prefix = "/foo"
[http.services] [http.services]
[http.services.service1] [http.services.service1]
[http.services.service1.LoadBalancer] [http.services.service1.LoadBalancer]
[[http.services.service1.LoadBalancer.Servers]] [[http.services.service1.LoadBalancer.Servers]]
URL = "http://127.0.0.1:80" URL = "http://127.0.0.1:80"
``` ```
## Advanced Configuration ## Provider Namespace
When you declare a middleware, it lives in its `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. 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. 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 image: your-docker-image
labels: labels:
# Attach file@add-foo-prefix middleware (declared in file) # Attach add-foo-prefix@file middleware (declared in file)
- "traefik.http.routers.my-container.middlewares=file@add-foo-prefix" - "traefik.http.routers.my-container.middlewares=add-foo-prefix@file"
``` ```
## Available Middlewares ## Available Middlewares

View file

@ -5,41 +5,57 @@ Reading What's Happening
By default, logs are written to stdout, in text format. By default, logs are written to stdout, in text format.
## Configuration Example ## Configuration
??? 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
### General ### General
Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). 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. By default, the logs are written to the standard output.
You can configure a file path instead using the `filePath` option. 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. 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 ## Log Rotation

View file

@ -26,7 +26,7 @@ traefik [--flag=flag_argument] [-f [flag_argument]]
traefik [--flag[=true|false| ]] [-f [true|false| ]] traefik [--flag[=true|false| ]] [-f [true|false| ]]
``` ```
### healthcheck ### `healthcheck`
Calls Traefik `/ping` to check the health of Traefik. Calls Traefik `/ping` to check the health of Traefik.
Its exit status is `0` if Traefik is healthy and `1` otherwise. Its exit status is `0` if Traefik is healthy and `1` otherwise.
@ -50,12 +50,12 @@ $ traefik healthcheck
OK: http://:8082/ping OK: http://:8082/ping
``` ```
### version ### `version`
Shows the current Traefik version. Shows the current Traefik version.
Usage: Usage:
```bash ```bash
traefik version [command] [flags] [arguments] traefik version
``` ```

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) Enabling the docker provider (Swarm Mode)
```toml ```toml
[docker] [providers.docker]
# swarm classic (1.12-) # swarm classic (1.12-)
# endpoint = "tcp://127.0.0.1:2375" # endpoint = "tcp://127.0.0.1:2375"
# docker swarm mode (1.12+) # docker swarm mode (1.12+)
@ -193,8 +193,8 @@ The container service name can be accessed as the `Name` identifier,
and the template has access to all the labels defined on this container. and the template has access to all the labels defined on this container.
```toml tab="File" ```toml tab="File"
[docker] [providers.docker]
defaultRule = "" defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
# ... # ...
``` ```
@ -215,6 +215,48 @@ _Optional, Default=15_
Defines the polling interval (in seconds) in Swarm Mode. Defines the polling interval (in seconds) in Swarm Mode.
### `constraints`
_Optional, Default=""_
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
That is to say, if none of the container's labels match the expression, no route for the container is created.
If the expression is empty, all detected containers are included.
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
??? example "Constraints Expression Examples"
```toml
# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
```
```toml
# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
```
```toml
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
```
```toml
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
```
```toml
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
```
```toml
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
```
## Routing Configuration Options ## Routing Configuration Options
### General ### General
@ -286,10 +328,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
This option overrides the value of `exposedByDefault`. This option overrides the value of `exposedByDefault`.
#### `traefik.tags`
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
#### `traefik.docker.network` #### `traefik.docker.network`
Overrides the default docker network to use for connections to the container. Overrides the default docker network to use for connections to the container.

View file

@ -230,6 +230,51 @@ spec:
More information about available middlewares in the dedicated [middlewares section](../middlewares/overview.md). 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 ### 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`: 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. If set, it overrides the Authorization header.
```toml tab="File" ```toml tab="File"
[marathon] [providers.marathon]
dcosToken = "xxxxxx" dcosToken = "xxxxxx"
# ... # ...
``` ```
@ -101,8 +101,8 @@ The app ID can be accessed as the Name identifier,
and the template has access to all the labels defined on this Marathon application. and the template has access to all the labels defined on this Marathon application.
```toml tab="File" ```toml tab="File"
[marathon] [providers.marathon]
defaultRule = "" defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
# ... # ...
``` ```
@ -132,7 +132,7 @@ Marathon server endpoint.
You can optionally specify multiple endpoints: You can optionally specify multiple endpoints:
```toml tab="File" ```toml tab="File"
[marathon] [providers.marathon]
endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080" endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
# ... # ...
``` ```
@ -150,16 +150,59 @@ Exposes Marathon applications by default through Traefik.
If set to false, applications that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration. If set to false, applications that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
### `filterMarathonConstraints` ### `constraints`
_Optional, Default=false_ _Optional, Default=""_
Enables filtering using Marathon constraints. Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
That is to say, if none of the application's labels match the expression, no route for the application is created.
In addition, the expression also matched against the application's constraints, such as described in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html).
If the expression is empty, all detected applications are included.
If enabled, Traefik will take into account Marathon constraints, as defined in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html). The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")`, as well as the usual boolean logic.
In addition, to match against marathon constraints, the function `MarathonConstraint("field:operator:value")` can be used, where the field, operator, and value parts are joined together in a single string with the `:` separator.
Each individual constraint will be treated as a verbatim compounded tag, ??? example "Constraints Expression Examples"
e.g. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":".
```toml
# Includes only applications having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
```
```toml
# Excludes applications having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
```
```toml
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
```
```toml
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
```
```toml
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
```
```toml
# Includes only applications having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
```
```toml
# Includes only applications having a Marathon constraint with field `A`, operator `B`, and value `C`.
constraints = "MarathonConstraint(`A:B:C`)"
```
```toml
# Uses both Marathon constraint and application label with logical operator.
constraints = "MarathonConstraint(`A:B:C`) && Label(`a.label.name`, `value`)"
```
### `forceTaskHostname` ### `forceTaskHostname`
@ -318,10 +361,6 @@ You can declare TCP Routers and/or Services using labels.
Setting this option controls whether Traefik exposes the application. Setting this option controls whether Traefik exposes the application.
It overrides the value of `exposedByDefault`. It overrides the value of `exposedByDefault`.
#### `traefik.tags`
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
#### `traefik.marathon.ipadressidx` #### `traefik.marathon.ipadressidx`
If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select. If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select.

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. Below is the list of the currently supported providers in Traefik.
| Provider | Type | Configuration Type | | Provider | Type | Configuration Type |
|---------------------------------|--------------|--------------------| |-----------------------------------|--------------|--------------------|
| [Docker](./docker.md) | Orchestrator | Label | | [Docker](./docker.md) | Orchestrator | Label |
| [File](./file.md) | Orchestrator | Custom Annotation | | [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource |
| [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource | | [Marathon](./marathon.md) | Orchestrator | Label |
| [Marathon](marathon.md) | Orchestrator | Label | | [Rancher](./rancher.md) | Orchestrator | Label |
| [File](./file.md) | Manual | TOML format |
!!! note "More Providers" !!! note "More Providers"
The current version of Traefik is in development and doesn't support (yet) every provider. See the previous version (1.7) for more providers. The current version of Traefik is in development and doesn't support (yet) every provider.
See the previous version (1.7) for more providers.
<!-- <!--
TODO (document TCP VS HTTP dynamic configuration) TODO (document TCP VS HTTP dynamic configuration)
--> -->
## Constraints Configuration
If you want to limit the scope of Traefik's service discovery, you can set constraints. ## Restrict the Scope of Service Discovery
Doing so, Traefik will create routes for containers that match these constraints only.
??? example "Containers with the api Tag" By default Traefik will create routes for all detected containers.
```toml If you want to limit the scope of Traefik's service discovery,
constraints = ["tag==api"] 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 List of providers that support that feature:
constraints = ["tag!=api"]
```
??? example "Containers with tags starting with 'us-'"
```toml - [Docker](./docker.md#exposedbydefault)
constraints = ["tag==us-*"] - [Rancher](./rancher.md#exposedbydefault)
``` - [Marathon](./marathon.md#exposedbydefault)
??? example "Multiple constraints" ### Constraints
```toml List of providers that support constraints:
# Multiple constraints
# - "tag==" must match with at least one tag
# - "tag!=" must match with none of tags
constraints = ["tag!=us-*", "tag!=asia-*"]
```
??? note "List of Providers that Support Constraints" - [Docker](./docker.md#constraints)
- [Rancher](./rancher.md#constraints)
- Docker - [Marathon](./marathon.md#constraints)
- Consul K/V - [Kubernetes CRD](./kubernetes-crd.md#labelselector)
- 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"]
```

View file

@ -19,64 +19,23 @@ Attach labels to your services and let Traefik do the rest!
Enabling the rancher provider Enabling the rancher provider
```toml ```toml
[provider.rancher] [Providers.Rancher]
``` ```
Attaching labels to services Attaching labels to services
```yaml ```yaml
labels: labels:
- traefik.http.services.my-service.rule=Host(my-domain) - traefik.http.services.my-service.rule=Host(`my-domain`)
``` ```
## Provider Configuration Options ## Provider Configuration Options
!!! tip "Browse the Reference" ??? tip "Browse the Reference"
If you're in a hurry, maybe you'd rather go through the configuration reference: If you're in a hurry, maybe you'd rather go through the configuration reference:
```toml ```toml
################################################################ --8<-- "content/providers/rancher.toml"
# Rancher Provider
################################################################
# Enable Rancher Provider.
[rancher]
# Expose Rancher services by default in Traefik.
#
# Optional
#
ExposedByDefault = "true"
# Enable watch Rancher changes.
#
# Optional
#
watch = true
# Filter services with unhealthy states and inactive states.
#
# Optional
#
EnableServiceHealthFilter = true
# Defines the polling interval (in seconds).
#
# Optional
#
RefreshSeconds = true
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
#
# Optional
#
IntervalPoll = false
# Prefix used for accessing the Rancher metadata service
#
# Optional
#
Prefix = 15
``` ```
### `ExposedByDefault` ### `ExposedByDefault`
@ -99,8 +58,8 @@ The service name can be accessed as the `Name` identifier,
and the template has access to all the labels defined on this container. and the template has access to all the labels defined on this container.
```toml tab="File" ```toml tab="File"
[rancher] [Providers.Rancher]
defaultRule = "" defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
# ... # ...
``` ```
@ -136,6 +95,50 @@ _Optional, Default=/latest_
Prefix used for accessing the Rancher metadata service Prefix used for accessing the Rancher metadata service
### `constraints`
_Optional, Default=""_
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
That is to say, if none of the container's labels match the expression, no route for the container is created.
If the expression is empty, all detected containers are included.
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
??? example "Constraints Expression Examples"
```toml
# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
```
```toml
# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
```
```toml
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
```
```toml
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
```
```toml
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
```
```toml
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
```
## Routing Configuration Options
### General ### General
Traefik creates, for each rancher service, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md). Traefik creates, for each rancher service, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md).
@ -185,10 +188,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
This option overrides the value of `exposedByDefault`. This option overrides the value of `exposedByDefault`.
#### `traefik.tags`
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
#### Port Lookup #### Port Lookup
Traefik is now capable of detecting the port to use, by following the default rancher flow. Traefik is now capable of detecting the port to use, by following the default rancher flow.

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 singular: middleware
scope: Namespaced 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 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
@ -85,6 +100,9 @@ spec:
# use an empty tls object for TLS with Let's Encrypt # use an empty tls object for TLS with Let's Encrypt
tls: tls:
secretName: supersecret secretName: supersecret
options:
name: myTLSOption
namespace: default
--- ---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
@ -104,3 +122,6 @@ spec:
tls: tls:
secretName: foosecret secretName: foosecret
passthrough: false passthrough: false
options:
name: myTLSOption
namespace: default

View file

@ -1,4 +1,3 @@
--accesslog (Default: "false") --accesslog (Default: "false")
Access log settings. Access log settings.
@ -173,6 +172,9 @@
--hostresolver.resolvdepth (Default: "5") --hostresolver.resolvdepth (Default: "5")
The maximal depth of DNS recursive resolving The maximal depth of DNS recursive resolving
--log (Default: "false")
Traefik log settings.
--log.filepath (Default: "") --log.filepath (Default: "")
Traefik log file path. Stdout is used when omitted or empty. Traefik log file path. Stdout is used when omitted or empty.
@ -249,17 +251,8 @@
Enable Docker backend with default settings. Enable Docker backend with default settings.
--providers.docker.constraints (Default: "") --providers.docker.constraints (Default: "")
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the container's labels
to determine whether to create any route for that container.
--providers.docker.constraints[n].key (Default: "")
The provider label that will be matched against. In practice, it is always
'tag'.
--providers.docker.constraints[n].mustmatch (Default: "false")
Whether the matching operator is equals or not equals.
--providers.docker.constraints[n].value (Default: "")
The value that will be matched against.
--providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)") --providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
Default rule. Default rule.
@ -382,17 +375,8 @@
Basic authentication Password. Basic authentication Password.
--providers.marathon.constraints (Default: "") --providers.marathon.constraints (Default: "")
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the application's
labels to determine whether to create any route for that application.
--providers.marathon.constraints[n].key (Default: "")
The provider label that will be matched against. In practice, it is always
'tag'.
--providers.marathon.constraints[n].mustmatch (Default: "false")
Whether the matching operator is equals or not equals.
--providers.marathon.constraints[n].value (Default: "")
The value that will be matched against.
--providers.marathon.dcostoken (Default: "") --providers.marathon.dcostoken (Default: "")
DCOSToken for DCOS environment, This will override the Authorization header. DCOSToken for DCOS environment, This will override the Authorization header.
@ -409,9 +393,6 @@
--providers.marathon.exposedbydefault (Default: "true") --providers.marathon.exposedbydefault (Default: "true")
Expose Marathon apps by default. Expose Marathon apps by default.
--providers.marathon.filtermarathonconstraints (Default: "false")
Enable use of Marathon constraints in constraint filtering.
--providers.marathon.forcetaskhostname (Default: "false") --providers.marathon.forcetaskhostname (Default: "false")
Force to use the task's hostname. Force to use the task's hostname.
@ -457,17 +438,8 @@
Enable Rancher backend with default settings. Enable Rancher backend with default settings.
--providers.rancher.constraints (Default: "") --providers.rancher.constraints (Default: "")
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the container's labels
to determine whether to create any route for that container.
--providers.rancher.constraints[n].key (Default: "")
The provider label that will be matched against. In practice, it is always
'tag'.
--providers.rancher.constraints[n].mustmatch (Default: "false")
Whether the matching operator is equals or not equals.
--providers.rancher.constraints[n].value (Default: "")
The value that will be matched against.
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)") --providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
Default rule. Default rule.

View file

@ -165,6 +165,9 @@ resolv.conf used for DNS resolving (Default: ```/etc/resolv.conf```)
`TRAEFIK_HOSTRESOLVER_RESOLVDEPTH`: `TRAEFIK_HOSTRESOLVER_RESOLVDEPTH`:
The maximal depth of DNS recursive resolving (Default: ```5```) The maximal depth of DNS recursive resolving (Default: ```5```)
`TRAEFIK_LOG`:
Traefik log settings. (Default: "false")
`TRAEFIK_LOG_FILEPATH`: `TRAEFIK_LOG_FILEPATH`:
Traefik log file path. Stdout is used when omitted or empty. 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```) Enable Docker backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`: `TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`: `TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
@ -373,16 +367,7 @@ Basic authentication User.
Basic authentication Password. Basic authentication Password.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`: `TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`: `TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
DCOSToken for DCOS environment, This will override the Authorization header. DCOSToken for DCOS environment, This will override the Authorization header.
@ -399,9 +384,6 @@ Marathon server endpoint. You can also specify multiple endpoint for Marathon. (
`TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`: `TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`:
Expose Marathon apps by default. (Default: ```true```) Expose Marathon apps by default. (Default: ```true```)
`TRAEFIK_PROVIDERS_MARATHON_FILTERMARATHONCONSTRAINTS`:
Enable use of Marathon constraints in constraint filtering. (Default: ```false```)
`TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`: `TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`:
Force to use the task's hostname. (Default: ```false```) Force to use the task's hostname. (Default: ```false```)
@ -445,16 +427,7 @@ Backends throttle duration: minimum duration between 2 events from providers bef
Enable Rancher backend with default settings. (Default: ```false```) Enable Rancher backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`: `TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags. Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`: `TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)

View file

@ -41,16 +41,7 @@
SwarmMode = true SwarmMode = true
Network = "foobar" Network = "foobar"
SwarmModeRefreshSeconds = 42 SwarmModeRefreshSeconds = 42
Constraints = "foobar"
[[Providers.Docker.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[[Providers.Docker.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[Providers.Docker.TLS] [Providers.Docker.TLS]
CA = "foobar" CA = "foobar"
@ -73,23 +64,13 @@
DefaultRule = "foobar" DefaultRule = "foobar"
ExposedByDefault = true ExposedByDefault = true
DCOSToken = "foobar" DCOSToken = "foobar"
FilterMarathonConstraints = true
DialerTimeout = 42 DialerTimeout = 42
ResponseHeaderTimeout = 42 ResponseHeaderTimeout = 42
TLSHandshakeTimeout = 42 TLSHandshakeTimeout = 42
KeepAlive = 42 KeepAlive = 42
ForceTaskHostname = true ForceTaskHostname = true
RespectReadinessChecks = true RespectReadinessChecks = true
Constraints = "foobar"
[[Providers.Marathon.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[[Providers.Marathon.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[Providers.Marathon.TLS] [Providers.Marathon.TLS]
CA = "foobar" CA = "foobar"
@ -134,16 +115,7 @@
RefreshSeconds = 42 RefreshSeconds = 42
IntervalPoll = true IntervalPoll = true
Prefix = "foobar" Prefix = "foobar"
Constraints = "foobar"
[[Providers.Rancher.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[[Providers.Rancher.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[API] [API]
EntryPoint = "foobar" EntryPoint = "foobar"

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: See the complete reference for the list of available options:
```toml tab="File" ```toml tab="File"
[EntryPoints] [entryPoints]
[EntryPoints.EntryPoint0] [entryPoints.EntryPoint0]
Address = ":8888" Address = ":8888"
[EntryPoints.EntryPoint0.Transport] [entryPoints.EntryPoint0.Transport]
[EntryPoints.EntryPoint0.Transport.LifeCycle] [entryPoints.EntryPoint0.Transport.LifeCycle]
RequestAcceptGraceTimeout = 42 RequestAcceptGraceTimeout = 42
GraceTimeOut = 42 GraceTimeOut = 42
[EntryPoints.EntryPoint0.Transport.RespondingTimeouts] [entryPoints.EntryPoint0.Transport.RespondingTimeouts]
ReadTimeout = 42 ReadTimeout = 42
WriteTimeout = 42 WriteTimeout = 42
IdleTimeout = 42 IdleTimeout = 42
[EntryPoints.EntryPoint0.ProxyProtocol] [entryPoints.EntryPoint0.ProxyProtocol]
Insecure = true Insecure = true
TrustedIPs = ["foobar", "foobar"] TrustedIPs = ["foobar", "foobar"]
[EntryPoints.EntryPoint0.ForwardedHeaders] [entryPoints.EntryPoint0.ForwardedHeaders]
Insecure = true Insecure = true
TrustedIPs = ["foobar", "foobar"] TrustedIPs = ["foobar", "foobar"]
``` ```

View file

@ -36,8 +36,10 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
[entryPoints.mysql-default] [entryPoints.mysql-default]
address = ":80" address = ":80"
[entryPoints.mysql-default] [entryPoints.mysql-default]
address = ":3306" address = ":3306"
```
```toml
[tcp] [tcp]
[tcp.routers] [tcp.routers]
[tcp.routers.to-database] [tcp.routers.to-database]
@ -50,8 +52,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
### EntryPoints ### EntryPoints
If not specified, HTTP routers will accept requests from all defined entrypoints. 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 entrypoints, set the entrypoints option. If you want to limit the router scope to a set of entry points, set the `entryPoints` option.
??? example "Listens to Every EntryPoint" ??? 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] [entryPoints.other]
# ... # ...
```
```toml
[http.routers] [http.routers]
[http.routers.Router-1] [http.routers.Router-1]
# By default, routers listen to every entrypoints # 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] [entryPoints.other]
# ... # ...
```
```toml
[http.routers] [http.routers]
[http.routers.Router-1] [http.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web 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" ??? example "Host is traefik.io"
```toml ```toml
rule = "Host(`traefik.io`)" rule = "Host(`traefik.io`)"
``` ```
??? example "Host is traefik.io OR Host is containo.us AND path is /traefik" ??? example "Host is traefik.io OR Host is containo.us AND path is /traefik"
```toml ```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: The table below lists all the available matchers:
| Rule | Description | | Rule | Description |
@ -126,7 +133,7 @@ The table below lists all the available matchers:
!!! tip "Combining Matchers Using Operators and Parenthesis" !!! 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" !!! 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 [http.routers.Router-1.tls] # will terminate the TLS request
options = "foo" options = "foo"
[tlsOptions] [tlsOptions]
[tlsOptions.foo] [tlsOptions.foo]
minVersion = "VersionTLS12" 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 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. 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 ```toml
[entryPoints] [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] [entryPoints.other]
# ... # ...
```
```toml
[tcp.routers] [tcp.routers]
[tcp.routers.Router-1] [tcp.routers.Router-1]
# By default, routers listen to every entrypoints # 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) [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 ```toml
[entryPoints] [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] [entryPoints.other]
# ... # ...
```
```toml
[tcp.routers] [tcp.routers]
[tcp.routers.Router-1] [tcp.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web 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 [tcp.routers.Router-1.tls] # will terminate the TLS request
options = "foo" options = "foo"
[tlsOptions] [tlsOptions]
[tlsOptions.foo] [tlsOptions.foo]
minVersion = "VersionTLS12" 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 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`). - 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 ## k3s Docker-compose Configuration
Our starting point is the docker-compose configuration file, to start the k3s cluster. 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, checker.HasLen, 14)
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`) 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.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.ServiceURL], checker.HasPrefix, "\"http://")
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`) 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.OriginStatus], checker.Equals, v.code)
c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1)) 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.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`)
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`) 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 services := rtconf.Services
c.Assert(services, checker.HasLen, 1) c.Assert(services, checker.HasLen, 1)
for k, v := range services { 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) c.Assert(v.LoadBalancer.Servers, checker.HasLen, serviceCount)
// We could break here, but we don't just to keep us honest. // 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]] [[http.services.service2.LoadBalancer.Servers]]
URL = "http://127.0.0.1:9020" URL = "http://127.0.0.1:9020"
[[tls]] [[tls]]
[tls.certificate] [tls.certificate]
certFile = "fixtures/https/snitest.com.cert" certFile = "fixtures/https/snitest.com.cert"

View file

@ -41,3 +41,18 @@ spec:
plural: ingressroutetcps plural: ingressroutetcps
singular: ingressroutetcp singular: ingressroutetcp
scope: Namespaced 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: services:
- name: whoami - name: whoami
port: 80 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: services:
- name: whoamitcp - name: whoamitcp
port: 8080 port: 8080
tls:
options:
name: mytlsoption

View file

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

View file

@ -191,7 +191,7 @@ func (s *HTTPSSuite) TestWithTLSOptions(c *check.C) {
c.Assert(err.Error(), checker.Contains, "protocol version not supported") c.Assert(err.Error(), checker.Contains, "protocol version not supported")
// with unknown tls option // 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) c.Assert(err, checker.IsNil)
} }

View file

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

View file

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

View file

@ -1,13 +1,13 @@
{ {
"routers": { "routers": {
"kubernetes@whoami-test/whoami": { "whoami-test/whoami@kubernetes": {
"entryPoints": null, "entryPoints": null,
"service": "default/whoami/http", "service": "default/whoami/http",
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)" "rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)"
} }
}, },
"services": { "services": {
"kubernetes@default/whoami/http": { "default/whoami/http@kubernetes": {
"loadbalancer": { "loadbalancer": {
"servers": [ "servers": [
{ {
@ -20,7 +20,7 @@
"passHostHeader": true "passHostHeader": true
}, },
"usedBy": [ "usedBy": [
"kubernetes@whoami-test/whoami" "whoami-test/whoami@kubernetes"
], ],
"serverStatus": { "serverStatus": {
"http://10.42.0.2:80": "UP", "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)) err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil) 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) 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)) err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil) 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) 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)) err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }

View file

@ -7,7 +7,6 @@ import (
"github.com/containous/traefik/pkg/config/static" "github.com/containous/traefik/pkg/config/static"
"github.com/containous/traefik/pkg/ping" "github.com/containous/traefik/pkg/ping"
"github.com/containous/traefik/pkg/provider"
"github.com/containous/traefik/pkg/provider/acme" "github.com/containous/traefik/pkg/provider/acme"
acmeprovider "github.com/containous/traefik/pkg/provider/acme" acmeprovider "github.com/containous/traefik/pkg/provider/acme"
"github.com/containous/traefik/pkg/provider/docker" "github.com/containous/traefik/pkg/provider/docker"
@ -153,20 +152,7 @@ func TestDo_globalConfiguration(t *testing.T) {
} }
config.Providers.Docker = &docker.Provider{ config.Providers.Docker = &docker.Provider{
Constrainer: provider.Constrainer{ Constraints: `Label("foo", "bar")`,
Constraints: []*types.Constraint{
{
Key: "file Constraints Key 1",
Value: "file Constraints Regex 2",
MustMatch: true,
},
{
Key: "file Constraints Key 1",
Value: "file Constraints Regex 2",
MustMatch: true,
},
},
},
Watch: true, Watch: true,
Endpoint: "MyEndPoint", Endpoint: "MyEndPoint",
DefaultRule: "PathPrefix(`/`)", DefaultRule: "PathPrefix(`/`)",

View file

@ -156,6 +156,7 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
return return
} }
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage)) rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex]) 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), Provider: getProviderName(routerID),
} }
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) log.FromContext(request.Context()).Error(err)
@ -209,6 +212,7 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
return return
} }
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage)) rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex]) 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(), ServerStatus: service.GetAllStatus(),
} }
rw.Header().Add("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) log.FromContext(request.Context()).Error(err)
@ -262,6 +268,7 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
return return
} }
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage)) rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex]) 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), Provider: getProviderName(middlewareID),
} }
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) log.FromContext(request.Context()).Error(err)
@ -314,6 +323,7 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
return return
} }
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage)) rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex]) 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), Provider: getProviderName(routerID),
} }
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) log.FromContext(request.Context()).Error(err)
@ -366,6 +378,7 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
return return
} }
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage)) rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex]) 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), Provider: getProviderName(serviceID),
} }
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) log.FromContext(request.Context()).Error(err)
@ -414,6 +429,8 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
TCPServices: h.runtimeConfiguration.TCPServices, TCPServices: h.runtimeConfiguration.TCPServices,
} }
rw.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(rw).Encode(result) err := json.NewEncoder(rw).Encode(result)
if err != nil { if err != nil {
log.FromContext(request.Context()).Error(err) 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 { 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", path: "/api/tcp/routers",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{ TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@test": { "test@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)", Rule: "Host(`foo.bar.other`)",
TLS: &config.RouterTCPTLSConfig{ TLS: &config.RouterTCPTLSConfig{
Passthrough: false, Passthrough: false,
}, },
}, },
}, },
"myprovider@bar": { "bar@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
}, },
}, },
@ -77,24 +77,24 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/routers?page=2&per_page=1", path: "/api/tcp/routers?page=2&per_page=1",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{ TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": { "bar@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
}, },
}, },
"myprovider@baz": { "baz@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`toto.bar`)", Rule: "Host(`toto.bar`)",
}, },
}, },
"myprovider@test": { "test@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)", Rule: "Host(`foo.bar.other`)",
}, },
}, },
@ -108,13 +108,13 @@ func TestHandlerTCP_API(t *testing.T) {
}, },
{ {
desc: "one TCP router by id", desc: "one TCP router by id",
path: "/api/tcp/routers/myprovider@bar", path: "/api/tcp/routers/bar@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{ TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": { "bar@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
}, },
}, },
@ -127,13 +127,13 @@ func TestHandlerTCP_API(t *testing.T) {
}, },
{ {
desc: "one TCP router by id, that does not exist", 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{ conf: config.RuntimeConfiguration{
TCPRouters: map[string]*config.TCPRouterInfo{ TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@bar": { "bar@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
}, },
}, },
@ -145,7 +145,7 @@ func TestHandlerTCP_API(t *testing.T) {
}, },
{ {
desc: "one TCP router by id, but no config", desc: "one TCP router by id, but no config",
path: "/api/tcp/routers/myprovider@bar", path: "/api/tcp/routers/bar@myprovider",
conf: config.RuntimeConfiguration{}, conf: config.RuntimeConfiguration{},
expected: expected{ expected: expected{
statusCode: http.StatusNotFound, statusCode: http.StatusNotFound,
@ -166,7 +166,7 @@ func TestHandlerTCP_API(t *testing.T) {
path: "/api/tcp/services", path: "/api/tcp/services",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{ TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": { "bar@myprovider": {
TCPService: &config.TCPService{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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", path: "/api/tcp/services?page=2&per_page=1",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{ TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": { "bar@myprovider": {
TCPService: &config.TCPService{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ Servers: []config.TCPServer{
@ -248,10 +248,10 @@ func TestHandlerTCP_API(t *testing.T) {
}, },
{ {
desc: "one tcp service by id", desc: "one tcp service by id",
path: "/api/tcp/services/myprovider@bar", path: "/api/tcp/services/bar@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{ TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": { "bar@myprovider": {
TCPService: &config.TCPService{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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", 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{ conf: config.RuntimeConfiguration{
TCPServices: map[string]*config.TCPServiceInfo{ TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@bar": { "bar@myprovider": {
TCPService: &config.TCPService{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ 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", desc: "one tcp service by id, but no config",
path: "/api/tcp/services/myprovider@foo", path: "/api/tcp/services/foo@myprovider",
conf: config.RuntimeConfiguration{}, conf: config.RuntimeConfiguration{},
expected: expected{ expected: expected{
statusCode: http.StatusNotFound, statusCode: http.StatusNotFound,
@ -326,6 +326,8 @@ func TestHandlerTCP_API(t *testing.T) {
return return
} }
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body) contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err) require.NoError(t, err)
@ -379,20 +381,20 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/routers", path: "/api/http/routers",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{ Routers: map[string]*config.RouterInfo{
"myprovider@test": { "test@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)", Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"}, Middlewares: []string{"addPrefixTest", "auth"},
}, },
}, },
"myprovider@bar": { "bar@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", 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", path: "/api/http/routers?page=2&per_page=1",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{ Routers: map[string]*config.RouterInfo{
"myprovider@bar": { "bar@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"}, Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
}, },
}, },
"myprovider@baz": { "baz@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`toto.bar`)", Rule: "Host(`toto.bar`)",
}, },
}, },
"myprovider@test": { "test@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)", Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"}, Middlewares: []string{"addPrefixTest", "auth"},
}, },
@ -473,15 +475,15 @@ func TestHandlerHTTP_API(t *testing.T) {
}, },
{ {
desc: "one router by id", desc: "one router by id",
path: "/api/http/routers/myprovider@bar", path: "/api/http/routers/bar@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{ Routers: map[string]*config.RouterInfo{
"myprovider@bar": { "bar@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", 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", desc: "one router by id, that does not exist",
path: "/api/http/routers/myprovider@foo", path: "/api/http/routers/foo@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Routers: map[string]*config.RouterInfo{ Routers: map[string]*config.RouterInfo{
"myprovider@bar": { "bar@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", 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", desc: "one router by id, but no config",
path: "/api/http/routers/myprovider@foo", path: "/api/http/routers/foo@myprovider",
conf: config.RuntimeConfiguration{}, conf: config.RuntimeConfiguration{},
expected: expected{ expected: expected{
statusCode: http.StatusNotFound, statusCode: http.StatusNotFound,
@ -533,7 +535,7 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/services", path: "/api/http/services",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{ Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo { "bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.1", "UP")
return si return si
}(), }(),
"myprovider@baz": func() *config.ServiceInfo { "baz@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.2", "UP")
return si return si
@ -578,7 +580,7 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/services?page=2&per_page=1", path: "/api/http/services?page=2&per_page=1",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{ Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo { "bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.1", "UP")
return si return si
}(), }(),
"myprovider@baz": func() *config.ServiceInfo { "baz@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.2", "UP")
return si return si
}(), }(),
"myprovider@test": func() *config.ServiceInfo { "test@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.4", "UP")
return si return si
@ -636,10 +638,10 @@ func TestHandlerHTTP_API(t *testing.T) {
}, },
{ {
desc: "one service by id", desc: "one service by id",
path: "/api/http/services/myprovider@bar", path: "/api/http/services/bar@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{ Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo { "bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.1", "UP")
return si return si
@ -664,10 +666,10 @@ func TestHandlerHTTP_API(t *testing.T) {
}, },
{ {
desc: "one service by id, that does not exist", desc: "one service by id, that does not exist",
path: "/api/http/services/myprovider@nono", path: "/api/http/services/nono@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{ Services: map[string]*config.ServiceInfo{
"myprovider@bar": func() *config.ServiceInfo { "bar@myprovider": func() *config.ServiceInfo {
si := &config.ServiceInfo{ si := &config.ServiceInfo{
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ 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") si.UpdateStatus("http://127.0.0.1", "UP")
return si return si
@ -691,7 +693,7 @@ func TestHandlerHTTP_API(t *testing.T) {
}, },
{ {
desc: "one service by id, but no config", desc: "one service by id, but no config",
path: "/api/http/services/myprovider@foo", path: "/api/http/services/foo@myprovider",
conf: config.RuntimeConfiguration{}, conf: config.RuntimeConfiguration{},
expected: expected{ expected: expected{
statusCode: http.StatusNotFound, statusCode: http.StatusNotFound,
@ -712,29 +714,29 @@ func TestHandlerHTTP_API(t *testing.T) {
path: "/api/http/middlewares", path: "/api/http/middlewares",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{ Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": { "auth@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{ BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"}, Users: []string{"admin:admin"},
}, },
}, },
UsedBy: []string{"myprovider@bar", "myprovider@test"}, UsedBy: []string{"bar@myprovider", "test@myprovider"},
}, },
"myprovider@addPrefixTest": { "addPrefixTest@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/titi", Prefix: "/titi",
}, },
}, },
UsedBy: []string{"myprovider@test"}, UsedBy: []string{"test@myprovider"},
}, },
"anotherprovider@addPrefixTest": { "addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/toto", 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", path: "/api/http/middlewares?page=2&per_page=1",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{ Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": { "auth@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{ BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"}, Users: []string{"admin:admin"},
}, },
}, },
UsedBy: []string{"myprovider@bar", "myprovider@test"}, UsedBy: []string{"bar@myprovider", "test@myprovider"},
}, },
"myprovider@addPrefixTest": { "addPrefixTest@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/titi", Prefix: "/titi",
}, },
}, },
UsedBy: []string{"myprovider@test"}, UsedBy: []string{"test@myprovider"},
}, },
"anotherprovider@addPrefixTest": { "addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/toto", 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", desc: "one middleware by id",
path: "/api/http/middlewares/myprovider@auth", path: "/api/http/middlewares/auth@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{ Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": { "auth@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{ BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"}, Users: []string{"admin:admin"},
}, },
}, },
UsedBy: []string{"myprovider@bar", "myprovider@test"}, UsedBy: []string{"bar@myprovider", "test@myprovider"},
}, },
"myprovider@addPrefixTest": { "addPrefixTest@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/titi", Prefix: "/titi",
}, },
}, },
UsedBy: []string{"myprovider@test"}, UsedBy: []string{"test@myprovider"},
}, },
"anotherprovider@addPrefixTest": { "addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/toto", 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", desc: "one middleware by id, that does not exist",
path: "/api/http/middlewares/myprovider@foo", path: "/api/http/middlewares/foo@myprovider",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Middlewares: map[string]*config.MiddlewareInfo{ Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": { "auth@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{ BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"}, 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", desc: "one middleware by id, but no config",
path: "/api/http/middlewares/myprovider@foo", path: "/api/http/middlewares/foo@myprovider",
conf: config.RuntimeConfiguration{}, conf: config.RuntimeConfiguration{},
expected: expected{ expected: expected{
statusCode: http.StatusNotFound, statusCode: http.StatusNotFound,
@ -869,6 +871,7 @@ func TestHandlerHTTP_API(t *testing.T) {
return return
} }
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body) contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err) require.NoError(t, err)
@ -912,7 +915,7 @@ func TestHandler_Configuration(t *testing.T) {
path: "/api/rawdata", path: "/api/rawdata",
conf: config.RuntimeConfiguration{ conf: config.RuntimeConfiguration{
Services: map[string]*config.ServiceInfo{ Services: map[string]*config.ServiceInfo{
"myprovider@foo-service": { "foo-service@myprovider": {
Service: &config.Service{ Service: &config.Service{
LoadBalancer: &config.LoadBalancerService{ LoadBalancer: &config.LoadBalancerService{
Servers: []config.Server{ Servers: []config.Server{
@ -925,21 +928,21 @@ func TestHandler_Configuration(t *testing.T) {
}, },
}, },
Middlewares: map[string]*config.MiddlewareInfo{ Middlewares: map[string]*config.MiddlewareInfo{
"myprovider@auth": { "auth@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
BasicAuth: &config.BasicAuth{ BasicAuth: &config.BasicAuth{
Users: []string{"admin:admin"}, Users: []string{"admin:admin"},
}, },
}, },
}, },
"myprovider@addPrefixTest": { "addPrefixTest@myprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/titi", Prefix: "/titi",
}, },
}, },
}, },
"anotherprovider@addPrefixTest": { "addPrefixTest@anotherprovider": {
Middleware: &config.Middleware{ Middleware: &config.Middleware{
AddPrefix: &config.AddPrefix{ AddPrefix: &config.AddPrefix{
Prefix: "/toto", Prefix: "/toto",
@ -948,25 +951,25 @@ func TestHandler_Configuration(t *testing.T) {
}, },
}, },
Routers: map[string]*config.RouterInfo{ Routers: map[string]*config.RouterInfo{
"myprovider@bar": { "bar@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)", Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"}, Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
}, },
}, },
"myprovider@test": { "test@myprovider": {
Router: &config.Router{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar.other`)", Rule: "Host(`foo.bar.other`)",
Middlewares: []string{"addPrefixTest", "auth"}, Middlewares: []string{"addPrefixTest", "auth"},
}, },
}, },
}, },
TCPServices: map[string]*config.TCPServiceInfo{ TCPServices: map[string]*config.TCPServiceInfo{
"myprovider@tcpfoo-service": { "tcpfoo-service@myprovider": {
TCPService: &config.TCPService{ TCPService: &config.TCPService{
LoadBalancer: &config.TCPLoadBalancerService{ LoadBalancer: &config.TCPLoadBalancerService{
Servers: []config.TCPServer{ Servers: []config.TCPServer{
@ -979,23 +982,22 @@ func TestHandler_Configuration(t *testing.T) {
}, },
}, },
TCPRouters: map[string]*config.TCPRouterInfo{ TCPRouters: map[string]*config.TCPRouterInfo{
"myprovider@tcpbar": { "tcpbar@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@tcpfoo-service", Service: "tcpfoo-service@myprovider",
Rule: "HostSNI(`foo.bar`)", Rule: "HostSNI(`foo.bar`)",
}, },
}, },
"myprovider@tcptest": { "tcptest@myprovider": {
TCPRouter: &config.TCPRouter{ TCPRouter: &config.TCPRouter{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@tcpfoo-service", Service: "tcpfoo-service@myprovider",
Rule: "HostSNI(`foo.bar.other`)", Rule: "HostSNI(`foo.bar.other`)",
}, },
}, },
}, },
}, },
expected: expected{ expected: expected{
statusCode: http.StatusOK, statusCode: http.StatusOK,
json: "testdata/getrawdata.json", json: "testdata/getrawdata.json",
@ -1011,6 +1013,7 @@ func TestHandler_Configuration(t *testing.T) {
// TODO: server status // TODO: server status
rtConf := &test.conf rtConf := &test.conf
rtConf.PopulateUsedBy() rtConf.PopulateUsedBy()
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf) handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
router := mux.NewRouter() router := mux.NewRouter()
@ -1022,6 +1025,7 @@ func TestHandler_Configuration(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, test.expected.statusCode, resp.StatusCode) assert.Equal(t, test.expected.statusCode, resp.StatusCode)
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
contents, err := ioutil.ReadAll(resp.Body) contents, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err) require.NoError(t, err)
@ -1054,10 +1058,10 @@ func TestHandler_Configuration(t *testing.T) {
func generateHTTPRouters(nbRouters int) map[string]*config.RouterInfo { func generateHTTPRouters(nbRouters int) map[string]*config.RouterInfo {
routers := make(map[string]*config.RouterInfo, nbRouters) routers := make(map[string]*config.RouterInfo, nbRouters)
for i := 0; i < nbRouters; i++ { 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{ Router: &config.Router{
EntryPoints: []string{"web"}, EntryPoints: []string{"web"},
Service: "myprovider@foo-service", Service: "foo-service@myprovider",
Rule: "Host(`foo.bar" + strconv.Itoa(i) + "`)", Rule: "Host(`foo.bar" + strconv.Itoa(i) + "`)",
}, },
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -128,5 +128,5 @@ func contains(cmds []*Command, name string) bool {
} }
func isFlag(arg 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. // Load loads the command's configuration from the environment variables.
func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) { func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
return e.load(os.Environ(), cmd) vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, cmd.Configuration)
} if len(vars) == 0 {
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 {
return false, nil return false, nil
} }
if err := env.Decode(environ, cmd.Configuration); err != nil { if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err) 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.") 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. // Load loads the command's configuration from flag arguments.
func (*FlagLoader) Load(args []string, cmd *Command) (bool, error) { 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 { if err := flag.Decode(args, cmd.Configuration); err != nil {
return false, fmt.Errorf("failed to decode configuration from flags: %v", err) 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 package env
import ( import (
"fmt"
"regexp"
"strings" "strings"
"github.com/containous/traefik/pkg/config/parser" "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. // Decode decodes the given environment variables into the given element.
// The operation goes through four stages roughly summarized as: // The operation goes through four stages roughly summarized as:
// env vars -> map // env vars -> map
// map -> tree of untyped nodes // map -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element) // untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed 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) vars := make(map[string]string)
for _, evr := range environ { for _, evr := range environ {
n := strings.SplitN(evr, "=", 2) 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]), "_", ".") key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
vars[key] = n[1] 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. // 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 return nil, nil
} }
node, err := parser.EncodeToNode(element, false) node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,3 +58,17 @@ func Encode(element interface{}) ([]parser.Flat, error) {
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"}) 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.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
err := Decode(test.environ, test.element) err := Decode(test.environ, DefaultNamePrefix, test.element)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, test.expected, test.element) assert.Equal(t, test.expected, test.element)
@ -460,39 +460,3 @@ func TestEncode(t *testing.T) {
assert.Equal(t, expected, flats) 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 nil, err
} }
return decodeRawToNode(data, filters...) return decodeRawToNode(data, parser.DefaultRootName, filters...)
default: default:
return nil, fmt.Errorf("unsupported file extension: %s", filePath) return nil, fmt.Errorf("unsupported file extension: %s", filePath)
} }
return decodeRawToNode(data, filters...) return decodeRawToNode(data, parser.DefaultRootName, filters...)
} }
func getRootFieldNames(element interface{}) []string { func getRootFieldNames(element interface{}) []string {

View file

@ -248,7 +248,6 @@ func Test_decodeFileToNode_Toml(t *testing.T) {
{Name: "DialerTimeout", Value: "42"}, {Name: "DialerTimeout", Value: "42"},
{Name: "Endpoint", Value: "foobar"}, {Name: "Endpoint", Value: "foobar"},
{Name: "ExposedByDefault", Value: "true"}, {Name: "ExposedByDefault", Value: "true"},
{Name: "FilterMarathonConstraints", Value: "true"},
{Name: "ForceTaskHostname", Value: "true"}, {Name: "ForceTaskHostname", Value: "true"},
{Name: "KeepAlive", Value: "42"}, {Name: "KeepAlive", Value: "42"},
{Name: "RespectReadinessChecks", Value: "true"}, {Name: "RespectReadinessChecks", Value: "true"},
@ -518,7 +517,6 @@ func Test_decodeFileToNode_Yaml(t *testing.T) {
{Name: "DialerTimeout", Value: "42"}, {Name: "DialerTimeout", Value: "42"},
{Name: "Endpoint", Value: "foobar"}, {Name: "Endpoint", Value: "foobar"},
{Name: "ExposedByDefault", Value: "true"}, {Name: "ExposedByDefault", Value: "true"},
{Name: "FilterMarathonConstraints", Value: "true"},
{Name: "ForceTaskHostname", Value: "true"}, {Name: "ForceTaskHostname", Value: "true"},
{Name: "KeepAlive", Value: "42"}, {Name: "KeepAlive", Value: "42"},
{Name: "RespectReadinessChecks", Value: "true"}, {Name: "RespectReadinessChecks", Value: "true"},

View file

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

View file

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

View file

@ -9,9 +9,9 @@ import (
"github.com/containous/traefik/pkg/config/parser" "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{ root := &parser.Node{
Name: "traefik", Name: rootName,
} }
vData := reflect.ValueOf(data) 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.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
node, err := decodeRawToNode(test.data) node, err := decodeRawToNode(test.data, parser.DefaultRootName)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, test.expected, node) assert.Equal(t, test.expected, node)

View file

@ -17,7 +17,7 @@ func Decode(args []string, element interface{}) error {
return err 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. // 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 return nil, nil
} }
node, err := parser.EncodeToNode(element, false) node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
"github.com/containous/traefik/pkg/config/parser"
) )
// Parse parses the command-line flag arguments into a map, // 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) { func (f *flagSet) setValue(name string, value string) {
n := strings.ToLower("traefik." + name) n := strings.ToLower(parser.DefaultRootName + "." + name)
v, ok := f.values[n] v, ok := f.values[n]
if ok && f.flagTypes[name] == reflect.Slice { 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{}, 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 { if err != nil {
return nil, err return nil, err
} }
@ -23,11 +23,11 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
// EncodeConfiguration converts a configuration to labels. // EncodeConfiguration converts a configuration to labels.
func EncodeConfiguration(conf *config.Configuration) (map[string]string, error) { 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. // Decode converts the labels to an element.
// labels -> [ node -> node + metadata (type) ] -> element (node) // labels -> [ node -> node + metadata (type) ] -> element (node)
func Decode(labels map[string]string, element interface{}, filters ...string) error { 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. // EncodeToNode converts an element to a node.
// element -> nodes // element -> nodes
func EncodeToNode(element interface{}, omitEmpty bool) (*Node, error) { func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node, error) {
rValue := reflect.ValueOf(element) rValue := reflect.ValueOf(element)
node := &Node{Name: "traefik"} node := &Node{Name: rootName}
encoder := encoderToNode{omitEmpty: omitEmpty} encoder := encoderToNode{omitEmpty: omitEmpty}

View file

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

View file

@ -6,18 +6,16 @@ import (
"strings" "strings"
) )
const labelRoot = "traefik"
// DecodeToNode converts the labels to a tree of nodes. // DecodeToNode converts the labels to a tree of nodes.
// If any filters are present, labels which do not match the filters are skipped. // 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) sortedKeys := sortKeys(labels, filters)
var node *Node var node *Node
for i, key := range sortedKeys { for i, key := range sortedKeys {
split := strings.Split(key, ".") split := strings.Split(key, ".")
if split[0] != labelRoot { if split[0] != rootName {
return nil, fmt.Errorf("invalid label root %s", split[0]) 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.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
out, err := DecodeToNode(test.in, test.filters...) out, err := DecodeToNode(test.in, DefaultRootName, test.filters...)
if test.expected.error { if test.expected.error {
require.Error(t, err) require.Error(t, err)

View file

@ -2,6 +2,9 @@ package parser
import "reflect" 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. // MapNamePlaceholder is the placeholder for the map name.
const MapNamePlaceholder = "<name>" const MapNamePlaceholder = "<name>"

View file

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

View file

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

View file

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

View file

@ -55,7 +55,7 @@ type Configuration struct {
Ping *ping.Handler `description:"Enable ping." export:"true" label:"allowEmpty"` Ping *ping.Handler `description:"Enable ping." export:"true" label:"allowEmpty"`
// Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"` // 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"` AccessLog *types.AccessLog `description:"Access log settings." export:"true" label:"allowEmpty"`
Tracing *Tracing `description:"OpenTracing configuration." 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/config/label"
"github.com/containous/traefik/pkg/log" "github.com/containous/traefik/pkg/log"
"github.com/containous/traefik/pkg/provider" "github.com/containous/traefik/pkg/provider"
"github.com/containous/traefik/pkg/provider/constraints"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
) )
@ -123,10 +124,13 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
return false return false
} }
if ok, failingConstraint := p.MatchConstraints(container.ExtraConf.Tags); !ok { matches, err := constraints.Match(container.Labels, p.Constraints)
if failingConstraint != nil { if err != nil {
logger.Debugf("Container pruned by %q constraint", failingConstraint.String()) logger.Error("Error matching constraints expression: %v", err)
} return false
}
if !matches {
logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
return false return false
} }

View file

@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/containous/traefik/pkg/config" "github.com/containous/traefik/pkg/config"
"github.com/containous/traefik/pkg/types"
docker "github.com/docker/docker/api/types" docker "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
@ -339,7 +338,7 @@ func Test_buildConfiguration(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
containers []dockerData containers []dockerData
constraints []*types.Constraint constraints string
expected *config.Configuration expected *config.Configuration
}{ }{
{ {
@ -1924,13 +1923,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
}, },
constraints: []*types.Constraint{ constraints: `Label("traefik.tags", "bar")`,
{
Key: "tag",
MustMatch: true,
Value: "bar",
},
},
expected: &config.Configuration{ expected: &config.Configuration{
TCP: &config.TCPConfiguration{ TCP: &config.TCPConfiguration{
Routers: map[string]*config.TCPRouter{}, Routers: map[string]*config.TCPRouter{},
@ -1965,13 +1958,7 @@ func Test_buildConfiguration(t *testing.T) {
}, },
}, },
}, },
constraints: []*types.Constraint{ constraints: `Label("traefik.tags", "foo")`,
{
Key: "tag",
MustMatch: true,
Value: "foo",
},
},
expected: &config.Configuration{ expected: &config.Configuration{
TCP: &config.TCPConfiguration{ TCP: &config.TCPConfiguration{
Routers: map[string]*config.TCPRouter{}, Routers: map[string]*config.TCPRouter{},

View file

@ -45,7 +45,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider. // Provider holds configurations of the provider.
type Provider struct { type Provider struct {
provider.Constrainer `description:"List of constraints used to filter out some containers." export:"true"` Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." export:"true"`
Watch bool `description:"Watch provider." export:"true"` Watch bool `description:"Watch provider." export:"true"`
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."` Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."`
DefaultRule string `description:"Default rule."` DefaultRule string `description:"Default rule."`

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. // configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider.
type configuration struct { type configuration struct {
Enable bool Enable bool
Tags []string
Docker specificConfiguration Docker specificConfiguration
} }
@ -31,7 +30,7 @@ func (p *Provider) getConfiguration(container dockerData) (configuration, error)
}, },
} }
err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable", "traefik.tags") err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable")
if err != nil { if err != nil {
return configuration{}, err return configuration{}, err
} }

View file

@ -49,6 +49,7 @@ type Client interface {
GetIngressRoutes() []*v1alpha1.IngressRoute GetIngressRoutes() []*v1alpha1.IngressRoute
GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP
GetMiddlewares() []*v1alpha1.Middleware GetMiddlewares() []*v1alpha1.Middleware
GetTLSOptions() []*v1alpha1.TLSOption
GetIngresses() []*extensionsv1beta1.Ingress GetIngresses() []*extensionsv1beta1.Ingress
GetService(namespace, name string) (*corev1.Service, bool, error) 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().IngressRoutes().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler) factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().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 := informers.NewFilteredSharedInformerFactory(c.csKube, resyncPeriod, ns, nil)
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler) factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
@ -241,6 +243,21 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
return result 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. // GetIngresses returns all Ingresses for observed namespaces in the cluster.
func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress { func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress {
var result []*extensionsv1beta1.Ingress var result []*extensionsv1beta1.Ingress

View file

@ -37,6 +37,7 @@ type clientMock struct {
ingressRoutes []*v1alpha1.IngressRoute ingressRoutes []*v1alpha1.IngressRoute
ingressRouteTCPs []*v1alpha1.IngressRouteTCP ingressRouteTCPs []*v1alpha1.IngressRouteTCP
middlewares []*v1alpha1.Middleware middlewares []*v1alpha1.Middleware
tlsOptions []*v1alpha1.TLSOption
watchChan chan interface{} watchChan chan interface{}
} }
@ -63,6 +64,8 @@ func newClientMock(paths ...string) clientMock {
c.ingressRouteTCPs = append(c.ingressRouteTCPs, o) c.ingressRouteTCPs = append(c.ingressRouteTCPs, o)
case *v1alpha1.Middleware: case *v1alpha1.Middleware:
c.middlewares = append(c.middlewares, o) c.middlewares = append(c.middlewares, o)
case *v1alpha1.TLSOption:
c.tlsOptions = append(c.tlsOptions, o)
case *v1beta12.Ingress: case *v1beta12.Ingress:
c.ingresses = append(c.ingresses, o) c.ingresses = append(c.ingresses, o)
case *corev1.Secret: case *corev1.Secret:
@ -88,6 +91,20 @@ func (c clientMock) GetMiddlewares() []*v1alpha1.Middleware {
return c.middlewares 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 { func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
return c.ingresses 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