Merge branch 'v2.0' into master
This commit is contained in:
commit
15b5433f1a
153 changed files with 3978 additions and 1308 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -5,7 +5,7 @@ The issue tracker is for reporting bugs and feature requests only.
|
|||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://slack.traefik.io
|
||||
- the Traefik community forum: https://community.containo.us/
|
||||
|
||||
-->
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
|
@ -11,7 +11,7 @@ The issue tracker is for reporting bugs and feature requests only.
|
|||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://slack.traefik.io
|
||||
- the Traefik community forum: https://community.containo.us/
|
||||
|
||||
-->
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
|
@ -11,7 +11,7 @@ The issue tracker is for reporting bugs and feature requests only.
|
|||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://slack.traefik.io
|
||||
- the Traefik community forum: https://community.containo.us/
|
||||
|
||||
-->
|
||||
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
|||
# Change Log
|
||||
|
||||
## [v2.0.0-alpha7](https://github.com/containous/traefik/tree/v2.0.0-alpha7) (2019-06-21)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha6...v2.0.0-alpha7)
|
||||
|
||||
**Enhancements:**
|
||||
- **[api]** API: new contract ([#4964](https://github.com/containous/traefik/pull/4964) by [mpl](https://github.com/mpl))
|
||||
- **[k8s,k8s/crd,tls]** Define TLS options on the Router configuration for Kubernetes ([#4973](https://github.com/containous/traefik/pull/4973) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware,provider]** Change the provider separator from . to @ ([#4982](https://github.com/containous/traefik/pull/4982) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Use name@provider instead of provider@name. ([#4990](https://github.com/containous/traefik/pull/4990) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** New constraints management. ([#4965](https://github.com/containous/traefik/pull/4965) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[cli]** Fix some CLI bugs ([#4989](https://github.com/containous/traefik/pull/4989) by [ldez](https://github.com/ldez))
|
||||
- **[cli]** Filter env vars configuration ([#4985](https://github.com/containous/traefik/pull/4985) by [ldez](https://github.com/ldez))
|
||||
- **[cli]** Return an error when help is called on a non existing command. ([#4977](https://github.com/containous/traefik/pull/4977) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Fix panic in TLS stores handling ([#4997](https://github.com/containous/traefik/pull/4997) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme,tls]** docs: rewrite of the HTTPS and TLS section ([#4980](https://github.com/containous/traefik/pull/4980) by [mpl](https://github.com/mpl))
|
||||
- Improve various parts of the documentation. ([#4996](https://github.com/containous/traefik/pull/4996) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.0.0-alpha6](https://github.com/containous/traefik/tree/v2.0.0-alpha6) (2019-06-18)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha5...v2.0.0-alpha6)
|
||||
|
||||
|
|
15
Gopkg.lock
generated
15
Gopkg.lock
generated
|
@ -1415,14 +1415,6 @@
|
|||
pruneopts = "NUT"
|
||||
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:09d61699d553a4e6ec998ad29816177b1f3d3ed0c18fe923d2c174ec065c99c8"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "256dc444b735e061061cf46c809487313d5b0065"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:253f275bd72c42f8d234712d1574c8b222fe9b72838bfaca11b21ace9c0e3d0a"
|
||||
name = "github.com/sacloud/libsacloud"
|
||||
|
@ -1598,12 +1590,12 @@
|
|||
revision = "3d629cff40b7040e0519628e7774ed11a95d9aff"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ca6bac407fedc14fbeeba861dd33a821ba3a1624c10126ec6003b0a28d4139c5"
|
||||
digest = "1:b9d8cc221fb40078c7eb78d73b1702b5b548511b3d62bbd56b2f8180089c79af"
|
||||
name = "github.com/vulcand/predicate"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "939c094524d124c55fa8afe0e077701db4a865e2"
|
||||
version = "v1.0.0"
|
||||
revision = "8fbfb3ab0e94276b6b58bec378600829adc7a203"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -2289,7 +2281,6 @@
|
|||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"github.com/prometheus/client_model/go",
|
||||
"github.com/rancher/go-rancher-metadata/metadata",
|
||||
"github.com/ryanuber/go-glob",
|
||||
"github.com/sirupsen/logrus",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
|
|
|
@ -146,10 +146,6 @@ required = [
|
|||
name = "github.com/rancher/go-rancher-metadata"
|
||||
source = "github.com/containous/go-rancher-metadata"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Masterminds/sprig"
|
||||
version = "2.19.0"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](http://goreportcard.com/report/containous/traefik)
|
||||
[![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
|
||||
[![Join the community support forum at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
|
||||
[![Twitter](https://img.shields.io/twitter/follow/traefik.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
|
||||
|
@ -103,7 +103,7 @@ A collection of contributions around Traefik can be found at [https://awesome.tr
|
|||
## Support
|
||||
|
||||
To get community support, you can:
|
||||
- join the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
|
||||
- join the Traefik community forum: [![Join the chat at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
|
||||
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||
|
||||
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||
|
|
|
@ -44,7 +44,7 @@ func main() {
|
|||
// traefik config inits
|
||||
tConfig := cmd.NewTraefikConfiguration()
|
||||
|
||||
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}}
|
||||
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
|
||||
|
||||
cmdTraefik := &cli.Command{
|
||||
Name: "traefik",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
|
||||
* During the Contributions Daily Meeting
|
||||
* By an assigned maintainer
|
||||
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, etc)
|
||||
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, Discourse, etc)
|
||||
|
||||
## PR review process:
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ To save us some time and get quicker feedback, be sure to follow the guide lines
|
|||
|
||||
For end-user related support questions, try using first:
|
||||
|
||||
- the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
|
||||
- the Traefik community forum: [![Join the chat at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||
|
||||
## Issue Title
|
||||
|
|
|
@ -10,10 +10,10 @@ Configuration in Traefik can refer to two different things:
|
|||
- The fully dynamic routing configuration (referred to as the _dynamic configuration_)
|
||||
- The startup configuration (referred to as the _static configuration_)
|
||||
|
||||
Elements in the _static configuration_ set up connections to [providers](../../providers/overview/) and define the [entrypoints](../../routing/entrypoints/) Traefik will listen to (these elements don't change often).
|
||||
Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
|
||||
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interuption or connection loss.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
||||
|
||||
!!! warning "Incompatible Configuration"
|
||||
Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now.
|
||||
|
@ -36,8 +36,8 @@ Traefik gets its _dynamic configuration_ from [providers](../providers/overview.
|
|||
There are three different, mutually exclusive, ways to define static configuration options in Traefik:
|
||||
|
||||
- In a configuration file
|
||||
- As environment variables
|
||||
- In the command-line arguments
|
||||
- As environment variables
|
||||
|
||||
These ways are evaluated in the order listed above.
|
||||
|
||||
|
@ -70,6 +70,12 @@ docker run traefik[:version] --help
|
|||
# ex: docker run traefik:2.0 --help
|
||||
```
|
||||
|
||||
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All available environment variables can be found [here](../reference/static-configuration/env.md)
|
||||
|
||||
## Available Configuration Options
|
||||
|
||||
All the configuration options are documented in their related section.
|
||||
|
|
|
@ -16,9 +16,13 @@ Pieces of middleware can be combined in chains to fit every scenario.
|
|||
```yaml tab="Docker"
|
||||
# As a Docker Label
|
||||
whoami:
|
||||
image: containous/whoami # A container that exposes an API to show its IP address
|
||||
# A container that exposes an API to show its IP address
|
||||
image: containous/whoami
|
||||
labels:
|
||||
# Create a middleware named `foo-add-prefix`
|
||||
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
|
||||
# Apply the middleware named `foo-add-prefix` to the router named `router1`
|
||||
- "traefik.http.router.router1.Middlewares=foo-add-prefix@docker"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
|
@ -61,14 +65,44 @@ spec:
|
|||
|
||||
```json tab="Marathon"
|
||||
"labels": {
|
||||
"traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo"
|
||||
"traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo",
|
||||
"traefik.http.router.router1.Middlewares": "foo-add-prefix@marathon"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
# As a Rancher Label
|
||||
labels:
|
||||
# Create a middleware named `foo-add-prefix`
|
||||
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
|
||||
# Apply the middleware named `foo-add-prefix` to the router named `router1`
|
||||
- "traefik.http.router.router1.Middlewares=foo-add-prefix@rancher"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tlsoptions.traefik.containo.us
|
||||
|
||||
spec:
|
||||
group: traefik.containo.us
|
||||
version: v1alpha1
|
||||
names:
|
||||
kind: TLSOption
|
||||
plural: tlsoptions
|
||||
singular: tlsoption
|
||||
scope: Namespaced
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: mytlsoption
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minversion: VersionTLS12
|
||||
```
|
||||
|
||||
```toml tab="File"
|
||||
|
@ -83,25 +117,30 @@ labels:
|
|||
Rule = "Host(`example.com`)"
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.foo-add-prefix.AddPrefix]
|
||||
[http.middlewares.foo-add-prefix.AddPrefix]
|
||||
prefix = "/foo"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1]
|
||||
[http.services.service1.LoadBalancer]
|
||||
[http.services.service1]
|
||||
[http.services.service1.LoadBalancer]
|
||||
|
||||
[[http.services.service1.LoadBalancer.Servers]]
|
||||
URL = "http://127.0.0.1:80"
|
||||
[[http.services.service1.LoadBalancer.Servers]]
|
||||
URL = "http://127.0.0.1:80"
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
## Provider Namespace
|
||||
|
||||
When you declare a middleware, it lives in its `provider` namespace.
|
||||
For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker `provider` namespace.
|
||||
When you declare a middleware, it lives in its provider namespace.
|
||||
For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker provider namespace.
|
||||
|
||||
If you use multiple `providers` and wish to reference a middleware declared in another `provider`, then you'll have to prefix the middleware name with the `provider` name.
|
||||
If you use multiple providers and wish to reference a middleware declared in another provider,
|
||||
then you'll have to prefix the middleware name with the provider name.
|
||||
|
||||
??? abstract "Referencing a Middleware from Another Provider"
|
||||
```text
|
||||
<resource-name>@<provider-name>
|
||||
```
|
||||
|
||||
!!! abstract "Referencing a Middleware from Another Provider"
|
||||
|
||||
Declaring the add-foo-prefix in the file provider.
|
||||
|
||||
|
@ -121,8 +160,8 @@ If you use multiple `providers` and wish to reference a middleware declared in a
|
|||
image: your-docker-image
|
||||
|
||||
labels:
|
||||
# Attach file@add-foo-prefix middleware (declared in file)
|
||||
- "traefik.http.routers.my-container.middlewares=file@add-foo-prefix"
|
||||
# Attach add-foo-prefix@file middleware (declared in file)
|
||||
- "traefik.http.routers.my-container.middlewares=add-foo-prefix@file"
|
||||
```
|
||||
|
||||
## Available Middlewares
|
||||
|
|
|
@ -5,41 +5,57 @@ Reading What's Happening
|
|||
|
||||
By default, logs are written to stdout, in text format.
|
||||
|
||||
## Configuration Example
|
||||
|
||||
??? example "Writing Logs in a File"
|
||||
|
||||
```toml
|
||||
[log]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
??? example "Writing Logs in a File, in JSON"
|
||||
|
||||
```toml
|
||||
[log]
|
||||
filePath = "/path/to/log-file.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
## Configuration
|
||||
|
||||
### General
|
||||
|
||||
Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on).
|
||||
|
||||
#### filePath
|
||||
#### `filePath`
|
||||
|
||||
By default, the logs are written to the standard output.
|
||||
You can configure a file path instead using the `filePath` option.
|
||||
|
||||
#### format
|
||||
```toml tab="File"
|
||||
# Writing Logs to a File
|
||||
[log]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# Writing Logs to a File
|
||||
--log.filePath="/path/to/traefik.log"
|
||||
```
|
||||
|
||||
#### `format`
|
||||
|
||||
By default, the logs use a text format (`common`), but you can also ask for the `json` format in the `format` option.
|
||||
|
||||
#### log level
|
||||
```toml tab="File"
|
||||
# Writing Logs to a File, in JSON
|
||||
[log]
|
||||
filePath = "/path/to/log-file.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
By default, the `level` is set to `error`, but you can choose amongst `debug`, `panic`, `fatal`, `error`, `warn`, and `info`.
|
||||
```bash tab="CLI"
|
||||
# Writing Logs to a File, in JSON
|
||||
--log.filePath="/path/to/traefik.log"
|
||||
--log.format="json"
|
||||
```
|
||||
|
||||
#### `level`
|
||||
|
||||
By default, the `level` is set to `ERROR`. Alternative logging levels are `DEBUG`, `PANIC`, `FATAL`, `ERROR`, `WARN`, and `INFO`.
|
||||
|
||||
```toml tab="File"
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--log.level="DEBUG"
|
||||
```
|
||||
|
||||
## Log Rotation
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ traefik [--flag=flag_argument] [-f [flag_argument]]
|
|||
traefik [--flag[=true|false| ]] [-f [true|false| ]]
|
||||
```
|
||||
|
||||
### healthcheck
|
||||
### `healthcheck`
|
||||
|
||||
Calls Traefik `/ping` to check the health of Traefik.
|
||||
Its exit status is `0` if Traefik is healthy and `1` otherwise.
|
||||
|
@ -50,12 +50,12 @@ $ traefik healthcheck
|
|||
OK: http://:8082/ping
|
||||
```
|
||||
|
||||
### version
|
||||
### `version`
|
||||
|
||||
Shows the current Traefik version.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
traefik version [command] [flags] [arguments]
|
||||
traefik version
|
||||
```
|
||||
|
|
13
docs/content/providers/crd_tls_option.yml
Normal file
13
docs/content/providers/crd_tls_option.yml
Normal 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
|
|
@ -37,7 +37,7 @@ Attach labels to your containers and let Traefik do the rest!
|
|||
Enabling the docker provider (Swarm Mode)
|
||||
|
||||
```toml
|
||||
[docker]
|
||||
[providers.docker]
|
||||
# swarm classic (1.12-)
|
||||
# endpoint = "tcp://127.0.0.1:2375"
|
||||
# docker swarm mode (1.12+)
|
||||
|
@ -193,8 +193,8 @@ The container service name can be accessed as the `Name` identifier,
|
|||
and the template has access to all the labels defined on this container.
|
||||
|
||||
```toml tab="File"
|
||||
[docker]
|
||||
defaultRule = ""
|
||||
[providers.docker]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -215,6 +215,48 @@ _Optional, Default=15_
|
|||
|
||||
Defines the polling interval (in seconds) in Swarm Mode.
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
That is to say, if none of the container's labels match the expression, no route for the container is created.
|
||||
If the expression is empty, all detected containers are included.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
|
||||
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
## Routing Configuration Options
|
||||
|
||||
### General
|
||||
|
@ -286,10 +328,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
|
|||
|
||||
This option overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### `traefik.docker.network`
|
||||
|
||||
Overrides the default docker network to use for connections to the container.
|
||||
|
|
|
@ -230,6 +230,51 @@ spec:
|
|||
|
||||
More information about available middlewares in the dedicated [middlewares section](../middlewares/overview.md).
|
||||
|
||||
### Traefik TLS Option Definition
|
||||
|
||||
Additionally, to allow for the use of tls options in an IngressRoute, we defined the CRD below for the TLSOption kind.
|
||||
More information about TLS Options is available in the dedicated [TLS Configuration Options](../../https/tls/#tls-options).
|
||||
|
||||
```yaml
|
||||
--8<-- "content/providers/crd_tls_option.yml"
|
||||
```
|
||||
|
||||
Once the TLSOption kind has been registered with the Kubernetes cluster or defined in the File Provider, it can then be used in IngressRoute definitions, such as:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: mytlsoption
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minversion: VersionTLS12
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: ingressroutebar
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`bar.com`) && PathPrefix(`/stripit`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
options:
|
||||
name: mytlsoption
|
||||
namespace: default
|
||||
```
|
||||
|
||||
!!! note "TLS Option reference and namespace"
|
||||
If the optional `namespace` attribute is not set, the configuration will be applied with the namespace of the IngressRoute.
|
||||
|
||||
### TLS
|
||||
|
||||
To allow for TLS, we made use of the `Secret` kind, as it was already defined, and it can be directly used in an `IngressRoute`:
|
||||
|
|
|
@ -78,7 +78,7 @@ DCOSToken for DCOS environment.
|
|||
If set, it overrides the Authorization header.
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
[providers.marathon]
|
||||
dcosToken = "xxxxxx"
|
||||
# ...
|
||||
```
|
||||
|
@ -101,8 +101,8 @@ The app ID can be accessed as the Name identifier,
|
|||
and the template has access to all the labels defined on this Marathon application.
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
defaultRule = ""
|
||||
[providers.marathon]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -132,7 +132,7 @@ Marathon server endpoint.
|
|||
You can optionally specify multiple endpoints:
|
||||
|
||||
```toml tab="File"
|
||||
[marathon]
|
||||
[providers.marathon]
|
||||
endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
||||
# ...
|
||||
```
|
||||
|
@ -150,16 +150,59 @@ Exposes Marathon applications by default through Traefik.
|
|||
|
||||
If set to false, applications that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
|
||||
|
||||
### `filterMarathonConstraints`
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=""_
|
||||
|
||||
Enables filtering using Marathon constraints.
|
||||
Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
|
||||
That is to say, if none of the application's labels match the expression, no route for the application is created.
|
||||
In addition, the expression also matched against the application's constraints, such as described in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html).
|
||||
If the expression is empty, all detected applications are included.
|
||||
|
||||
If enabled, Traefik will take into account Marathon constraints, as defined in [Marathon constraints](https://mesosphere.github.io/marathon/docs/constraints.html).
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")`, as well as the usual boolean logic.
|
||||
In addition, to match against marathon constraints, the function `MarathonConstraint("field:operator:value")` can be used, where the field, operator, and value parts are joined together in a single string with the `:` separator.
|
||||
|
||||
Each individual constraint will be treated as a verbatim compounded tag,
|
||||
e.g. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":".
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only applications having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes applications having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only applications having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only applications having a Marathon constraint with field `A`, operator `B`, and value `C`.
|
||||
constraints = "MarathonConstraint(`A:B:C`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Uses both Marathon constraint and application label with logical operator.
|
||||
constraints = "MarathonConstraint(`A:B:C`) && Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
### `forceTaskHostname`
|
||||
|
||||
|
@ -318,10 +361,6 @@ You can declare TCP Routers and/or Services using labels.
|
|||
Setting this option controls whether Traefik exposes the application.
|
||||
It overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### `traefik.marathon.ipadressidx`
|
||||
|
||||
If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select.
|
||||
|
|
|
@ -26,74 +26,46 @@ Even if each provider is different, we can categorize them in four groups:
|
|||
|
||||
Below is the list of the currently supported providers in Traefik.
|
||||
|
||||
| Provider | Type | Configuration Type |
|
||||
|---------------------------------|--------------|--------------------|
|
||||
| [Docker](./docker.md) | Orchestrator | Label |
|
||||
| [File](./file.md) | Orchestrator | Custom Annotation |
|
||||
| [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource |
|
||||
| [Marathon](marathon.md) | Orchestrator | Label |
|
||||
| Provider | Type | Configuration Type |
|
||||
|-----------------------------------|--------------|--------------------|
|
||||
| [Docker](./docker.md) | Orchestrator | Label |
|
||||
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource |
|
||||
| [Marathon](./marathon.md) | Orchestrator | Label |
|
||||
| [Rancher](./rancher.md) | Orchestrator | Label |
|
||||
| [File](./file.md) | Manual | TOML format |
|
||||
|
||||
!!! note "More Providers"
|
||||
|
||||
The current version of Traefik is in development and doesn't support (yet) every provider. See the previous version (1.7) for more providers.
|
||||
The current version of Traefik is in development and doesn't support (yet) every provider.
|
||||
See the previous version (1.7) for more providers.
|
||||
|
||||
<!--
|
||||
TODO (document TCP VS HTTP dynamic configuration)
|
||||
-->
|
||||
TODO (document TCP VS HTTP dynamic configuration)
|
||||
-->
|
||||
|
||||
## Constraints Configuration
|
||||
## Restrict the Scope of Service Discovery
|
||||
|
||||
If you want to limit the scope of Traefik's service discovery, you can set constraints.
|
||||
Doing so, Traefik will create routes for containers that match these constraints only.
|
||||
By default Traefik will create routes for all detected containers.
|
||||
|
||||
??? example "Containers with the api Tag"
|
||||
If you want to limit the scope of Traefik's service discovery,
|
||||
i.e. disallow route creation for some containers,
|
||||
you can do so in two different ways:
|
||||
either with the generic configuration option `exposedByDefault`,
|
||||
or with a finer granularity mechanism based on constraints.
|
||||
|
||||
```toml
|
||||
constraints = ["tag==api"]
|
||||
```
|
||||
### `exposedByDefault` and `traefik.enable`
|
||||
|
||||
??? example "Containers without the api Tag"
|
||||
List of providers that support that feature:
|
||||
|
||||
```toml
|
||||
constraints = ["tag!=api"]
|
||||
```
|
||||
- [Docker](./docker.md#exposedbydefault)
|
||||
- [Rancher](./rancher.md#exposedbydefault)
|
||||
- [Marathon](./marathon.md#exposedbydefault)
|
||||
|
||||
??? example "Containers with tags starting with 'us-'"
|
||||
### Constraints
|
||||
|
||||
```toml
|
||||
constraints = ["tag==us-*"]
|
||||
```
|
||||
List of providers that support constraints:
|
||||
|
||||
??? example "Multiple constraints"
|
||||
|
||||
```toml
|
||||
# Multiple constraints
|
||||
# - "tag==" must match with at least one tag
|
||||
# - "tag!=" must match with none of tags
|
||||
constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||
```
|
||||
|
||||
??? note "List of Providers that Support Constraints"
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
- BoltDB
|
||||
- Zookeeper
|
||||
- ECS
|
||||
- Etcd
|
||||
- Consul Catalog
|
||||
- Rancher
|
||||
- Marathon
|
||||
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||
|
||||
!!! note
|
||||
|
||||
The constraint option belongs to the provider configuration itself.
|
||||
|
||||
??? example "Setting the Constraint Options for Docker"
|
||||
|
||||
```toml
|
||||
[providers]
|
||||
[providers.docker]
|
||||
constraints = ["tag==api"]
|
||||
```
|
||||
- [Docker](./docker.md#constraints)
|
||||
- [Rancher](./rancher.md#constraints)
|
||||
- [Marathon](./marathon.md#constraints)
|
||||
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)
|
||||
|
|
|
@ -19,64 +19,23 @@ Attach labels to your services and let Traefik do the rest!
|
|||
Enabling the rancher provider
|
||||
|
||||
```toml
|
||||
[provider.rancher]
|
||||
[Providers.Rancher]
|
||||
```
|
||||
|
||||
Attaching labels to services
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- traefik.http.services.my-service.rule=Host(my-domain)
|
||||
- traefik.http.services.my-service.rule=Host(`my-domain`)
|
||||
```
|
||||
|
||||
## Provider Configuration Options
|
||||
|
||||
!!! tip "Browse the Reference"
|
||||
??? tip "Browse the Reference"
|
||||
If you're in a hurry, maybe you'd rather go through the configuration reference:
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher Provider
|
||||
################################################################
|
||||
|
||||
# Enable Rancher Provider.
|
||||
[rancher]
|
||||
|
||||
# Expose Rancher services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
ExposedByDefault = "true"
|
||||
|
||||
# Enable watch Rancher changes.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Filter services with unhealthy states and inactive states.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
EnableServiceHealthFilter = true
|
||||
|
||||
# Defines the polling interval (in seconds).
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
RefreshSeconds = true
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
IntervalPoll = false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
Prefix = 15
|
||||
--8<-- "content/providers/rancher.toml"
|
||||
```
|
||||
|
||||
### `ExposedByDefault`
|
||||
|
@ -99,8 +58,8 @@ The service name can be accessed as the `Name` identifier,
|
|||
and the template has access to all the labels defined on this container.
|
||||
|
||||
```toml tab="File"
|
||||
[rancher]
|
||||
defaultRule = ""
|
||||
[Providers.Rancher]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -136,6 +95,50 @@ _Optional, Default=/latest_
|
|||
|
||||
Prefix used for accessing the Rancher metadata service
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
That is to say, if none of the container's labels match the expression, no route for the container is created.
|
||||
If the expression is empty, all detected containers are included.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegexp("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
|
||||
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegexp(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
## Routing Configuration Options
|
||||
|
||||
### General
|
||||
|
||||
Traefik creates, for each rancher service, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md).
|
||||
|
@ -185,10 +188,6 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
|
|||
|
||||
This option overrides the value of `exposedByDefault`.
|
||||
|
||||
#### `traefik.tags`
|
||||
|
||||
Sets the tags for [constraints filtering](./overview.md#constraints-configuration).
|
||||
|
||||
#### Port Lookup
|
||||
|
||||
Traefik is now capable of detecting the port to use, by following the default rancher flow.
|
||||
|
|
20
docs/content/providers/rancher.toml
Normal file
20
docs/content/providers/rancher.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Enable Rancher Provider.
|
||||
[Providers.Rancher]
|
||||
|
||||
# Expose Rancher services by default in Traefik.
|
||||
ExposedByDefault = true
|
||||
|
||||
# Enable watch Rancher changes.
|
||||
Watch = true
|
||||
|
||||
# Filter services with unhealthy states and inactive states.
|
||||
EnableServiceHealthFilter = true
|
||||
|
||||
# Defines the polling interval (in seconds).
|
||||
RefreshSeconds = true
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
|
||||
IntervalPoll = false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
Prefix = "/latest"
|
|
@ -26,6 +26,21 @@ spec:
|
|||
singular: middleware
|
||||
scope: Namespaced
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tlsoptions.traefik.containo.us
|
||||
|
||||
spec:
|
||||
group: traefik.containo.us
|
||||
version: v1alpha1
|
||||
names:
|
||||
kind: TLSOption
|
||||
plural: tlsoptions
|
||||
singular: tlsoption
|
||||
scope: Namespaced
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
|
@ -85,6 +100,9 @@ spec:
|
|||
# use an empty tls object for TLS with Let's Encrypt
|
||||
tls:
|
||||
secretName: supersecret
|
||||
options:
|
||||
name: myTLSOption
|
||||
namespace: default
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
|
@ -104,3 +122,6 @@ spec:
|
|||
tls:
|
||||
secretName: foosecret
|
||||
passthrough: false
|
||||
options:
|
||||
name: myTLSOption
|
||||
namespace: default
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
--accesslog (Default: "false")
|
||||
Access log settings.
|
||||
|
||||
|
@ -173,6 +172,9 @@
|
|||
--hostresolver.resolvdepth (Default: "5")
|
||||
The maximal depth of DNS recursive resolving
|
||||
|
||||
--log (Default: "false")
|
||||
Traefik log settings.
|
||||
|
||||
--log.filepath (Default: "")
|
||||
Traefik log file path. Stdout is used when omitted or empty.
|
||||
|
||||
|
@ -249,17 +251,8 @@
|
|||
Enable Docker backend with default settings.
|
||||
|
||||
--providers.docker.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.docker.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.docker.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.docker.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels
|
||||
to determine whether to create any route for that container.
|
||||
|
||||
--providers.docker.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||
Default rule.
|
||||
|
@ -382,17 +375,8 @@
|
|||
Basic authentication Password.
|
||||
|
||||
--providers.marathon.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.marathon.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.marathon.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.marathon.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the application's
|
||||
labels to determine whether to create any route for that application.
|
||||
|
||||
--providers.marathon.dcostoken (Default: "")
|
||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
||||
|
@ -409,9 +393,6 @@
|
|||
--providers.marathon.exposedbydefault (Default: "true")
|
||||
Expose Marathon apps by default.
|
||||
|
||||
--providers.marathon.filtermarathonconstraints (Default: "false")
|
||||
Enable use of Marathon constraints in constraint filtering.
|
||||
|
||||
--providers.marathon.forcetaskhostname (Default: "false")
|
||||
Force to use the task's hostname.
|
||||
|
||||
|
@ -457,17 +438,8 @@
|
|||
Enable Rancher backend with default settings.
|
||||
|
||||
--providers.rancher.constraints (Default: "")
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
--providers.rancher.constraints[n].key (Default: "")
|
||||
The provider label that will be matched against. In practice, it is always
|
||||
'tag'.
|
||||
|
||||
--providers.rancher.constraints[n].mustmatch (Default: "false")
|
||||
Whether the matching operator is equals or not equals.
|
||||
|
||||
--providers.rancher.constraints[n].value (Default: "")
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels
|
||||
to determine whether to create any route for that container.
|
||||
|
||||
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
|
||||
Default rule.
|
||||
|
|
|
@ -165,6 +165,9 @@ resolv.conf used for DNS resolving (Default: ```/etc/resolv.conf```)
|
|||
`TRAEFIK_HOSTRESOLVER_RESOLVDEPTH`:
|
||||
The maximal depth of DNS recursive resolving (Default: ```5```)
|
||||
|
||||
`TRAEFIK_LOG`:
|
||||
Traefik log settings. (Default: "false")
|
||||
|
||||
`TRAEFIK_LOG_FILEPATH`:
|
||||
Traefik log file path. Stdout is used when omitted or empty.
|
||||
|
||||
|
@ -241,16 +244,7 @@ Middleware list.
|
|||
Enable Docker backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
@ -373,16 +367,7 @@ Basic authentication User.
|
|||
Basic authentication Password.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
|
||||
DCOSToken for DCOS environment, This will override the Authorization header.
|
||||
|
@ -399,9 +384,6 @@ Marathon server endpoint. You can also specify multiple endpoint for Marathon. (
|
|||
`TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`:
|
||||
Expose Marathon apps by default. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_FILTERMARATHONCONSTRAINTS`:
|
||||
Enable use of Marathon constraints in constraint filtering. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`:
|
||||
Force to use the task's hostname. (Default: ```false```)
|
||||
|
||||
|
@ -445,16 +427,7 @@ Backends throttle duration: minimum duration between 2 events from providers bef
|
|||
Enable Rancher backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
|
||||
Filter services by constraint, matching with Traefik tags.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_KEY`:
|
||||
The provider label that will be matched against. In practice, it is always 'tag'.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_MUSTMATCH`:
|
||||
Whether the matching operator is equals or not equals. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_VALUE`:
|
||||
The value that will be matched against.
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
|
|
@ -41,16 +41,7 @@
|
|||
SwarmMode = true
|
||||
Network = "foobar"
|
||||
SwarmModeRefreshSeconds = 42
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[Providers.Docker.TLS]
|
||||
CA = "foobar"
|
||||
|
@ -73,23 +64,13 @@
|
|||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
DCOSToken = "foobar"
|
||||
FilterMarathonConstraints = true
|
||||
DialerTimeout = 42
|
||||
ResponseHeaderTimeout = 42
|
||||
TLSHandshakeTimeout = 42
|
||||
KeepAlive = 42
|
||||
ForceTaskHostname = true
|
||||
RespectReadinessChecks = true
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[Providers.Marathon.TLS]
|
||||
CA = "foobar"
|
||||
|
@ -134,16 +115,7 @@
|
|||
RefreshSeconds = 42
|
||||
IntervalPoll = true
|
||||
Prefix = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Regex = "foobar"
|
||||
Constraints = "foobar"
|
||||
|
||||
[API]
|
||||
EntryPoint = "foobar"
|
||||
|
|
|
@ -44,22 +44,22 @@ You can define them using a toml file, CLI arguments, or a key-value store.
|
|||
See the complete reference for the list of available options:
|
||||
|
||||
```toml tab="File"
|
||||
[EntryPoints]
|
||||
[entryPoints]
|
||||
|
||||
[EntryPoints.EntryPoint0]
|
||||
[entryPoints.EntryPoint0]
|
||||
Address = ":8888"
|
||||
[EntryPoints.EntryPoint0.Transport]
|
||||
[EntryPoints.EntryPoint0.Transport.LifeCycle]
|
||||
[entryPoints.EntryPoint0.Transport]
|
||||
[entryPoints.EntryPoint0.Transport.LifeCycle]
|
||||
RequestAcceptGraceTimeout = 42
|
||||
GraceTimeOut = 42
|
||||
[EntryPoints.EntryPoint0.Transport.RespondingTimeouts]
|
||||
[entryPoints.EntryPoint0.Transport.RespondingTimeouts]
|
||||
ReadTimeout = 42
|
||||
WriteTimeout = 42
|
||||
IdleTimeout = 42
|
||||
[EntryPoints.EntryPoint0.ProxyProtocol]
|
||||
[entryPoints.EntryPoint0.ProxyProtocol]
|
||||
Insecure = true
|
||||
TrustedIPs = ["foobar", "foobar"]
|
||||
[EntryPoints.EntryPoint0.ForwardedHeaders]
|
||||
[entryPoints.EntryPoint0.ForwardedHeaders]
|
||||
Insecure = true
|
||||
TrustedIPs = ["foobar", "foobar"]
|
||||
```
|
||||
|
|
|
@ -37,7 +37,9 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
|
|||
address = ":80"
|
||||
[entryPoints.mysql-default]
|
||||
address = ":3306"
|
||||
```
|
||||
|
||||
```toml
|
||||
[tcp]
|
||||
[tcp.routers]
|
||||
[tcp.routers.to-database]
|
||||
|
@ -50,8 +52,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
|
|||
|
||||
### EntryPoints
|
||||
|
||||
If not specified, HTTP routers will accept requests from all defined entrypoints.
|
||||
If you want to limit the router scope to a set of entrypoints, set the entrypoints option.
|
||||
If not specified, HTTP routers will accept requests from all defined entry points.
|
||||
If you want to limit the router scope to a set of entry points, set the `entryPoints` option.
|
||||
|
||||
??? example "Listens to Every EntryPoint"
|
||||
|
||||
|
@ -63,7 +65,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
|
|||
# ...
|
||||
[entryPoints.other]
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml
|
||||
[http.routers]
|
||||
[http.routers.Router-1]
|
||||
# By default, routers listen to every entrypoints
|
||||
|
@ -81,7 +85,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
|
|||
# ...
|
||||
[entryPoints.other]
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml
|
||||
[http.routers]
|
||||
[http.routers.Router-1]
|
||||
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
|
||||
|
@ -97,14 +103,15 @@ If the rule is verified, the router becomes active, calls middlewares, and then
|
|||
??? example "Host is traefik.io"
|
||||
|
||||
```toml
|
||||
rule = "Host(`traefik.io`)"
|
||||
rule = "Host(`traefik.io`)"
|
||||
```
|
||||
|
||||
??? example "Host is traefik.io OR Host is containo.us AND path is /traefik"
|
||||
|
||||
```toml
|
||||
rule = "Host(`traefik.io`) || (Host(`containo.us`) && Path(`/traefik`))"
|
||||
rule = "Host(`traefik.io`) || (Host(`containo.us`) && Path(`/traefik`))"
|
||||
```
|
||||
|
||||
The table below lists all the available matchers:
|
||||
|
||||
| Rule | Description |
|
||||
|
@ -126,7 +133,7 @@ The table below lists all the available matchers:
|
|||
|
||||
!!! tip "Combining Matchers Using Operators and Parenthesis"
|
||||
|
||||
You can combine multiple matchers using the AND (`&&`) and OR (`||) operators. You can also use parenthesis.
|
||||
You can combine multiple matchers using the AND (`&&`) and OR (`||`) operators. You can also use parenthesis.
|
||||
|
||||
!!! important "Rule, Middleware, and Services"
|
||||
|
||||
|
@ -212,7 +219,6 @@ It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied
|
|||
[http.routers.Router-1.tls] # will terminate the TLS request
|
||||
options = "foo"
|
||||
|
||||
|
||||
[tlsOptions]
|
||||
[tlsOptions.foo]
|
||||
minVersion = "VersionTLS12"
|
||||
|
@ -234,7 +240,7 @@ If no matching route is found for the TCP routers, then the HTTP routers will ta
|
|||
If not specified, TCP routers will accept requests from all defined entry points.
|
||||
If you want to limit the router scope to a set of entry points, set the entry points option.
|
||||
|
||||
??? example "Listens to Every EntryPoint"
|
||||
??? example "Listens to Every Entry Point"
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
|
@ -244,7 +250,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
# ...
|
||||
[entryPoints.other]
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml
|
||||
[tcp.routers]
|
||||
[tcp.routers.Router-1]
|
||||
# By default, routers listen to every entrypoints
|
||||
|
@ -253,7 +261,7 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
[tcp.routers.Router-1.tls] # will route TLS requests (and ignore non tls requests)
|
||||
```
|
||||
|
||||
??? example "Listens to Specific EntryPoints"
|
||||
??? example "Listens to Specific Entry Points"
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
|
@ -263,7 +271,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
# ...
|
||||
[entryPoints.other]
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml
|
||||
[tcp.routers]
|
||||
[tcp.routers.Router-1]
|
||||
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
|
||||
|
@ -340,7 +350,6 @@ It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied
|
|||
[tcp.routers.Router-1.tls] # will terminate the TLS request
|
||||
options = "foo"
|
||||
|
||||
|
||||
[tlsOptions]
|
||||
[tlsOptions.foo]
|
||||
minVersion = "VersionTLS12"
|
||||
|
|
|
@ -16,6 +16,10 @@ In the following, the Kubernetes resources defined in YAML configuration files c
|
|||
- the first, and usual way, is simply with the `kubectl apply` command.
|
||||
- the second, which can be used for this tutorial, is to directly place the files in the directory used by the k3s docker image for such inputs (`/var/lib/rancher/k3s/server/manifests`).
|
||||
|
||||
!!! important "Kubectl Version"
|
||||
|
||||
With the `rancher/k3s` version used in this guide (`0.5.0`), the kubectl version needs to be >= `0.11`.
|
||||
|
||||
## k3s Docker-compose Configuration
|
||||
|
||||
Our starting point is the docker-compose configuration file, to start the k3s cluster.
|
||||
|
|
|
@ -581,7 +581,7 @@ func CheckAccessLogFormat(c *check.C, line string, i int) {
|
|||
c.Assert(results, checker.HasLen, 14)
|
||||
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`)
|
||||
c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
|
||||
c.Assert(results[accesslog.RouterName], checker.HasPrefix, "\"docker@rt-")
|
||||
c.Assert(results[accesslog.RouterName], checker.Matches, `"rt-.+@docker"`)
|
||||
c.Assert(results[accesslog.ServiceURL], checker.HasPrefix, "\"http://")
|
||||
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
|
||||
}
|
||||
|
@ -596,7 +596,7 @@ func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue)
|
|||
}
|
||||
c.Assert(results[accesslog.OriginStatus], checker.Equals, v.code)
|
||||
c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
|
||||
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?(docker@)?`+v.routerName+`.*$`)
|
||||
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?`+v.routerName+`.*(@docker)?$`)
|
||||
c.Assert(results[accesslog.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`)
|
||||
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
|
|||
services := rtconf.Services
|
||||
c.Assert(services, checker.HasLen, 1)
|
||||
for k, v := range services {
|
||||
c.Assert(k, checker.Equals, "docker@"+composeService+"_integrationtest"+composeProject)
|
||||
c.Assert(k, checker.Equals, composeService+"_integrationtest"+composeProject+"@docker")
|
||||
c.Assert(v.LoadBalancer.Servers, checker.HasLen, serviceCount)
|
||||
// We could break here, but we don't just to keep us honest.
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ level = "DEBUG"
|
|||
[[http.services.service2.LoadBalancer.Servers]]
|
||||
URL = "http://127.0.0.1:9020"
|
||||
|
||||
|
||||
[[tls]]
|
||||
[tls.certificate]
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
|
|
|
@ -41,3 +41,18 @@ spec:
|
|||
plural: ingressroutetcps
|
||||
singular: ingressroutetcp
|
||||
scope: Namespaced
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tlsoptions.traefik.containo.us
|
||||
|
||||
spec:
|
||||
group: traefik.containo.us
|
||||
version: v1alpha1
|
||||
names:
|
||||
kind: TLSOption
|
||||
plural: tlsoptions
|
||||
singular: tlsoption
|
||||
scope: Namespaced
|
||||
|
|
|
@ -15,3 +15,7 @@ spec:
|
|||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
|
||||
tls:
|
||||
options:
|
||||
name: mytlsoption
|
||||
|
|
12
integration/fixtures/k8s/03-tlsoption.yml
Normal file
12
integration/fixtures/k8s/03-tlsoption.yml
Normal 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
|
|
@ -12,3 +12,6 @@ spec:
|
|||
services:
|
||||
- name: whoamitcp
|
||||
port: 8080
|
||||
tls:
|
||||
options:
|
||||
name: mytlsoption
|
||||
|
|
|
@ -13,7 +13,7 @@ level = "DEBUG"
|
|||
address = ":8001"
|
||||
|
||||
[api]
|
||||
middlewares = ["file@authentication"]
|
||||
middlewares = ["authentication@file"]
|
||||
|
||||
[ping]
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ func (s *HTTPSSuite) TestWithTLSOptions(c *check.C) {
|
|||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
||||
|
||||
// with unknown tls option
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("unknown TLS options: unknown"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("unknown TLS options: unknown@file"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
|
|
|
@ -444,8 +444,8 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
|
|||
Routers: map[string]*config.Router{
|
||||
"router1": {
|
||||
EntryPoints: []string{"web"},
|
||||
Middlewares: []string{"file@customheader"},
|
||||
Service: "file@service",
|
||||
Middlewares: []string{"customheader@file"},
|
||||
Service: "service@file",
|
||||
Rule: "PathPrefix(`/`)",
|
||||
},
|
||||
},
|
||||
|
|
53
integration/testdata/rawdata-crd.json
vendored
53
integration/testdata/rawdata-crd.json
vendored
|
@ -1,14 +1,17 @@
|
|||
{
|
||||
"routers": {
|
||||
"kubernetescrd@default/test-crd-6b204d94623b3df4370c": {
|
||||
"default/test-crd-6b204d94623b3df4370c@kubernetescrd": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"service": "default/test-crd-6b204d94623b3df4370c",
|
||||
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/bar`)",
|
||||
"priority": 12
|
||||
"priority": 12,
|
||||
"tls": {
|
||||
"options": "default/mytlsoption"
|
||||
}
|
||||
},
|
||||
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016": {
|
||||
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
|
@ -20,82 +23,86 @@
|
|||
}
|
||||
},
|
||||
"middlewares": {
|
||||
"kubernetescrd@default/stripprefix": {
|
||||
"default/stripprefix@kubernetescrd": {
|
||||
"stripPrefix": {
|
||||
"prefixes": [
|
||||
"/tobestripped"
|
||||
]
|
||||
},
|
||||
"usedBy": [
|
||||
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016"
|
||||
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd"
|
||||
]
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"kubernetescrd@default/test-crd-6b204d94623b3df4370c": {
|
||||
"default/test-crd-6b204d94623b3df4370c@kubernetescrd": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://10.42.0.4:80"
|
||||
"url": "http://10.42.0.2:80"
|
||||
},
|
||||
{
|
||||
"url": "http://10.42.0.5:80"
|
||||
"url": "http://10.42.0.6:80"
|
||||
}
|
||||
],
|
||||
"passHostHeader": true
|
||||
},
|
||||
"usedBy": [
|
||||
"kubernetescrd@default/test-crd-6b204d94623b3df4370c"
|
||||
"default/test-crd-6b204d94623b3df4370c@kubernetescrd"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.4:80": "UP",
|
||||
"http://10.42.0.5:80": "UP"
|
||||
"http://10.42.0.2:80": "UP",
|
||||
"http://10.42.0.6:80": "UP"
|
||||
}
|
||||
},
|
||||
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016": {
|
||||
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://10.42.0.4:80"
|
||||
"url": "http://10.42.0.2:80"
|
||||
},
|
||||
{
|
||||
"url": "http://10.42.0.5:80"
|
||||
"url": "http://10.42.0.6:80"
|
||||
}
|
||||
],
|
||||
"passHostHeader": true
|
||||
},
|
||||
"usedBy": [
|
||||
"kubernetescrd@default/test2-crd-23c7f4c450289ee29016"
|
||||
"default/test2-crd-23c7f4c450289ee29016@kubernetescrd"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.4:80": "UP",
|
||||
"http://10.42.0.5:80": "UP"
|
||||
"http://10.42.0.2:80": "UP",
|
||||
"http://10.42.0.6:80": "UP"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tcpRouters": {
|
||||
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a": {
|
||||
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd": {
|
||||
"entryPoints": [
|
||||
"footcp"
|
||||
],
|
||||
"service": "default/test3-crd-673acf455cb2dab0b43a",
|
||||
"rule": "HostSNI(`*`)"
|
||||
"rule": "HostSNI(`*`)",
|
||||
"tls": {
|
||||
"passthrough": false,
|
||||
"options": "default/mytlsoption"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tcpServices": {
|
||||
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a": {
|
||||
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "10.42.0.2:8080"
|
||||
"address": "10.42.0.3:8080"
|
||||
},
|
||||
{
|
||||
"address": "10.42.0.3:8080"
|
||||
"address": "10.42.0.4:8080"
|
||||
}
|
||||
]
|
||||
},
|
||||
"usedBy": [
|
||||
"kubernetescrd@default/test3-crd-673acf455cb2dab0b43a"
|
||||
"default/test3-crd-673acf455cb2dab0b43a@kubernetescrd"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
6
integration/testdata/rawdata-ingress.json
vendored
6
integration/testdata/rawdata-ingress.json
vendored
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"routers": {
|
||||
"kubernetes@whoami-test/whoami": {
|
||||
"whoami-test/whoami@kubernetes": {
|
||||
"entryPoints": null,
|
||||
"service": "default/whoami/http",
|
||||
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"kubernetes@default/whoami/http": {
|
||||
"default/whoami/http@kubernetes": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
|
@ -20,7 +20,7 @@
|
|||
"passHostHeader": true
|
||||
},
|
||||
"usedBy": [
|
||||
"kubernetes@whoami-test/whoami"
|
||||
"whoami-test/whoami@kubernetes"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.2:80": "UP",
|
||||
|
|
|
@ -89,7 +89,7 @@ func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/file@router1", "file@ratelimit"))
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit@file"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func (s *TracingSuite) TestZipkinRetry(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/file@router2", "file@retry"))
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,6 @@ func (s *TracingSuite) TestZipkinAuth(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "file@basic-auth"))
|
||||
err = try.GetRequest("http://"+s.ZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/ping"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/acme"
|
||||
acmeprovider "github.com/containous/traefik/pkg/provider/acme"
|
||||
"github.com/containous/traefik/pkg/provider/docker"
|
||||
|
@ -153,20 +152,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
config.Providers.Docker = &docker.Provider{
|
||||
Constrainer: provider.Constrainer{
|
||||
Constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "file Constraints Key 1",
|
||||
Value: "file Constraints Regex 2",
|
||||
MustMatch: true,
|
||||
},
|
||||
{
|
||||
Key: "file Constraints Key 1",
|
||||
Value: "file Constraints Regex 2",
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Constraints: `Label("foo", "bar")`,
|
||||
Watch: true,
|
||||
Endpoint: "MyEndPoint",
|
||||
DefaultRule: "PathPrefix(`/`)",
|
||||
|
|
|
@ -156,6 +156,7 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
|
||||
|
||||
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
|
||||
|
@ -180,6 +181,8 @@ func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
|
|||
Provider: getProviderName(routerID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -209,6 +212,7 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
|
||||
|
||||
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
|
||||
|
@ -234,6 +238,8 @@ func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
|
|||
ServerStatus: service.GetAllStatus(),
|
||||
}
|
||||
|
||||
rw.Header().Add("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -262,6 +268,7 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
|
||||
|
||||
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
|
||||
|
@ -286,6 +293,8 @@ func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
|
|||
Provider: getProviderName(middlewareID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -314,6 +323,7 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
|
||||
|
||||
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
|
||||
|
@ -338,6 +348,8 @@ func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
|
|||
Provider: getProviderName(routerID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -366,6 +378,7 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
|
||||
|
||||
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
|
||||
|
@ -390,6 +403,8 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
|
|||
Provider: getProviderName(serviceID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -414,6 +429,8 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
|
|||
TCPServices: h.runtimeConfiguration.TCPServices,
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
|
@ -464,5 +481,5 @@ func getIntParam(request *http.Request, key string, defaultValue int) (int, erro
|
|||
}
|
||||
|
||||
func getProviderName(id string) string {
|
||||
return strings.SplitN(id, ".", 2)[0]
|
||||
return strings.SplitN(id, "@", 2)[1]
|
||||
}
|
||||
|
|
|
@ -47,20 +47,20 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
path: "/api/tcp/routers",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -77,24 +77,24 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
path: "/api/tcp/routers?page=2&per_page=1",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"myprovider@baz": {
|
||||
"baz@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`toto.bar`)",
|
||||
},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
},
|
||||
},
|
||||
|
@ -108,13 +108,13 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one TCP router by id",
|
||||
path: "/api/tcp/routers/myprovider@bar",
|
||||
path: "/api/tcp/routers/bar@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -127,13 +127,13 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one TCP router by id, that does not exist",
|
||||
path: "/api/tcp/routers/myprovider@foo",
|
||||
path: "/api/tcp/routers/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -145,7 +145,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one TCP router by id, but no config",
|
||||
path: "/api/tcp/routers/myprovider@bar",
|
||||
path: "/api/tcp/routers/bar@myprovider",
|
||||
conf: config.RuntimeConfiguration{},
|
||||
expected: expected{
|
||||
statusCode: http.StatusNotFound,
|
||||
|
@ -166,7 +166,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
path: "/api/tcp/services",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -176,9 +176,9 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@baz": {
|
||||
"baz@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -188,7 +188,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -203,7 +203,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
path: "/api/tcp/services?page=2&per_page=1",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -213,9 +213,9 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@baz": {
|
||||
"baz@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -225,9 +225,9 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -248,10 +248,10 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one tcp service by id",
|
||||
path: "/api/tcp/services/myprovider@bar",
|
||||
path: "/api/tcp/services/bar@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -261,7 +261,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -272,10 +272,10 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one tcp service by id, that does not exist",
|
||||
path: "/api/tcp/services/myprovider@nono",
|
||||
path: "/api/tcp/services/nono@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -285,7 +285,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -295,7 +295,7 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one tcp service by id, but no config",
|
||||
path: "/api/tcp/services/myprovider@foo",
|
||||
path: "/api/tcp/services/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{},
|
||||
expected: expected{
|
||||
statusCode: http.StatusNotFound,
|
||||
|
@ -326,6 +326,8 @@ func TestHandlerTCP_API(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
|
||||
|
||||
contents, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -379,20 +381,20 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/routers",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -408,25 +410,25 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/routers?page=2&per_page=1",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
"myprovider@baz": {
|
||||
"baz@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`toto.bar`)",
|
||||
},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
|
@ -473,15 +475,15 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one router by id",
|
||||
path: "/api/http/routers/myprovider@bar",
|
||||
path: "/api/http/routers/bar@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -493,15 +495,15 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one router by id, that does not exist",
|
||||
path: "/api/http/routers/myprovider@foo",
|
||||
path: "/api/http/routers/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -512,7 +514,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one router by id, but no config",
|
||||
path: "/api/http/routers/myprovider@foo",
|
||||
path: "/api/http/routers/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{},
|
||||
expected: expected{
|
||||
statusCode: http.StatusNotFound,
|
||||
|
@ -533,7 +535,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/services",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@bar": func() *config.ServiceInfo {
|
||||
"bar@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -544,12 +546,12 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
}(),
|
||||
"myprovider@baz": func() *config.ServiceInfo {
|
||||
"baz@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -560,7 +562,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.2", "UP")
|
||||
return si
|
||||
|
@ -578,7 +580,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/services?page=2&per_page=1",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@bar": func() *config.ServiceInfo {
|
||||
"bar@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -589,12 +591,12 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
}(),
|
||||
"myprovider@baz": func() *config.ServiceInfo {
|
||||
"baz@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -605,12 +607,12 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.2", "UP")
|
||||
return si
|
||||
}(),
|
||||
"myprovider@test": func() *config.ServiceInfo {
|
||||
"test@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -621,7 +623,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.4", "UP")
|
||||
return si
|
||||
|
@ -636,10 +638,10 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one service by id",
|
||||
path: "/api/http/services/myprovider@bar",
|
||||
path: "/api/http/services/bar@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@bar": func() *config.ServiceInfo {
|
||||
"bar@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -650,7 +652,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
|
@ -664,10 +666,10 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one service by id, that does not exist",
|
||||
path: "/api/http/services/myprovider@nono",
|
||||
path: "/api/http/services/nono@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@bar": func() *config.ServiceInfo {
|
||||
"bar@myprovider": func() *config.ServiceInfo {
|
||||
si := &config.ServiceInfo{
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
|
@ -678,7 +680,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@foo", "myprovider@test"},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
}
|
||||
si.UpdateStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
|
@ -691,7 +693,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one service by id, but no config",
|
||||
path: "/api/http/services/myprovider@foo",
|
||||
path: "/api/http/services/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{},
|
||||
expected: expected{
|
||||
statusCode: http.StatusNotFound,
|
||||
|
@ -712,29 +714,29 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/middlewares",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@test"},
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -749,29 +751,29 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
path: "/api/http/middlewares?page=2&per_page=1",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@test"},
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -783,32 +785,32 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one middleware by id",
|
||||
path: "/api/http/middlewares/myprovider@auth",
|
||||
path: "/api/http/middlewares/auth@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@test"},
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -819,16 +821,16 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one middleware by id, that does not exist",
|
||||
path: "/api/http/middlewares/myprovider@foo",
|
||||
path: "/api/http/middlewares/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -838,7 +840,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "one middleware by id, but no config",
|
||||
path: "/api/http/middlewares/myprovider@foo",
|
||||
path: "/api/http/middlewares/foo@myprovider",
|
||||
conf: config.RuntimeConfiguration{},
|
||||
expected: expected{
|
||||
statusCode: http.StatusNotFound,
|
||||
|
@ -869,6 +871,7 @@ func TestHandlerHTTP_API(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
|
||||
contents, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -912,7 +915,7 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
path: "/api/rawdata",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -925,21 +928,21 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
|
@ -948,25 +951,25 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@tcpfoo-service": {
|
||||
"tcpfoo-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -979,23 +982,22 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@tcpbar": {
|
||||
"tcpbar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@tcpfoo-service",
|
||||
Service: "tcpfoo-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"myprovider@tcptest": {
|
||||
"tcptest@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@tcpfoo-service",
|
||||
Service: "tcpfoo-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar.other`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
json: "testdata/getrawdata.json",
|
||||
|
@ -1011,6 +1013,7 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
// TODO: server status
|
||||
|
||||
rtConf := &test.conf
|
||||
|
||||
rtConf.PopulateUsedBy()
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
router := mux.NewRouter()
|
||||
|
@ -1022,6 +1025,7 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected.statusCode, resp.StatusCode)
|
||||
assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
|
||||
|
||||
contents, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
@ -1054,10 +1058,10 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
func generateHTTPRouters(nbRouters int) map[string]*config.RouterInfo {
|
||||
routers := make(map[string]*config.RouterInfo, nbRouters)
|
||||
for i := 0; i < nbRouters; i++ {
|
||||
routers[fmt.Sprintf("myprovider@bar%2d", i)] = &config.RouterInfo{
|
||||
routers[fmt.Sprintf("bar%2d@myprovider", i)] = &config.RouterInfo{
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar" + strconv.Itoa(i) + "`)",
|
||||
},
|
||||
}
|
||||
|
|
44
pkg/api/testdata/getrawdata.json
vendored
44
pkg/api/testdata/getrawdata.json
vendored
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"routers": {
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"anotherprovider@addPrefixTest"
|
||||
"addPrefixTest@anotherprovider"
|
||||
],
|
||||
"service": "myprovider@foo-service",
|
||||
"service": "foo-service@myprovider",
|
||||
"rule": "Host(`foo.bar`)"
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
|
@ -19,41 +19,41 @@
|
|||
"addPrefixTest",
|
||||
"auth"
|
||||
],
|
||||
"service": "myprovider@foo-service",
|
||||
"service": "foo-service@myprovider",
|
||||
"rule": "Host(`foo.bar.other`)"
|
||||
}
|
||||
},
|
||||
"middlewares": {
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
"addPrefix": {
|
||||
"prefix": "/toto"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@bar"
|
||||
"bar@myprovider"
|
||||
]
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
"addPrefix": {
|
||||
"prefix": "/titi"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@test"
|
||||
"test@myprovider"
|
||||
]
|
||||
},
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
"basicAuth": {
|
||||
"users": [
|
||||
"admin:admin"
|
||||
]
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@bar",
|
||||
"myprovider@test"
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
|
@ -63,29 +63,29 @@
|
|||
"passHostHeader": false
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@bar",
|
||||
"myprovider@test"
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tcpRouters": {
|
||||
"myprovider@tcpbar": {
|
||||
"tcpbar@myprovider": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"service": "myprovider@tcpfoo-service",
|
||||
"service": "tcpfoo-service@myprovider",
|
||||
"rule": "HostSNI(`foo.bar`)"
|
||||
},
|
||||
"myprovider@tcptest": {
|
||||
"tcptest@myprovider": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"service": "myprovider@tcpfoo-service",
|
||||
"service": "tcpfoo-service@myprovider",
|
||||
"rule": "HostSNI(`foo.bar.other`)"
|
||||
}
|
||||
},
|
||||
"tcpServices": {
|
||||
"myprovider@tcpfoo-service": {
|
||||
"tcpfoo-service@myprovider": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
|
@ -94,8 +94,8 @@
|
|||
]
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@tcpbar",
|
||||
"myprovider@tcptest"
|
||||
"tcpbar@myprovider",
|
||||
"tcptest@myprovider"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
8
pkg/api/testdata/middleware-auth.json
vendored
8
pkg/api/testdata/middleware-auth.json
vendored
|
@ -4,10 +4,10 @@
|
|||
"admin:admin"
|
||||
]
|
||||
},
|
||||
"name": "myprovider@auth",
|
||||
"provider": "myprovider@auth",
|
||||
"name": "auth@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@bar",
|
||||
"myprovider@test"
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
6
pkg/api/testdata/middlewares-page2.json
vendored
6
pkg/api/testdata/middlewares-page2.json
vendored
|
@ -3,10 +3,10 @@
|
|||
"addPrefix": {
|
||||
"prefix": "/titi"
|
||||
},
|
||||
"name": "myprovider@addPrefixTest",
|
||||
"provider": "myprovider@addPrefixTest",
|
||||
"name": "addPrefixTest@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@test"
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
20
pkg/api/testdata/middlewares.json
vendored
20
pkg/api/testdata/middlewares.json
vendored
|
@ -3,20 +3,20 @@
|
|||
"addPrefix": {
|
||||
"prefix": "/toto"
|
||||
},
|
||||
"name": "anotherprovider@addPrefixTest",
|
||||
"provider": "anotherprovider@addPrefixTest",
|
||||
"name": "addPrefixTest@anotherprovider",
|
||||
"provider": "anotherprovider",
|
||||
"usedBy": [
|
||||
"myprovider@bar"
|
||||
"bar@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"addPrefix": {
|
||||
"prefix": "/titi"
|
||||
},
|
||||
"name": "myprovider@addPrefixTest",
|
||||
"provider": "myprovider@addPrefixTest",
|
||||
"name": "addPrefixTest@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@test"
|
||||
"test@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -25,11 +25,11 @@
|
|||
"admin:admin"
|
||||
]
|
||||
},
|
||||
"name": "myprovider@auth",
|
||||
"provider": "myprovider@auth",
|
||||
"name": "auth@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@bar",
|
||||
"myprovider@test"
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
8
pkg/api/testdata/router-bar.json
vendored
8
pkg/api/testdata/router-bar.json
vendored
|
@ -4,10 +4,10 @@
|
|||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"anotherprovider@addPrefixTest"
|
||||
"addPrefixTest@anotherprovider"
|
||||
],
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
30
pkg/api/testdata/routers-many-lastpage.json
vendored
30
pkg/api/testdata/routers-many-lastpage.json
vendored
|
@ -3,45 +3,45 @@
|
|||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar14",
|
||||
"provider": "myprovider@bar14",
|
||||
"name": "bar14@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar14`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar15",
|
||||
"provider": "myprovider@bar15",
|
||||
"name": "bar15@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar15`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar16",
|
||||
"provider": "myprovider@bar16",
|
||||
"name": "bar16@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar16`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar17",
|
||||
"provider": "myprovider@bar17",
|
||||
"name": "bar17@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar17`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar18",
|
||||
"provider": "myprovider@bar18",
|
||||
"name": "bar18@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar18`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
||||
]
|
6
pkg/api/testdata/routers-page2.json
vendored
6
pkg/api/testdata/routers-page2.json
vendored
|
@ -3,9 +3,9 @@
|
|||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`toto.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
||||
]
|
14
pkg/api/testdata/routers.json
vendored
14
pkg/api/testdata/routers.json
vendored
|
@ -5,12 +5,12 @@
|
|||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"anotherprovider@addPrefixTest"
|
||||
"addPrefixTest@anotherprovider"
|
||||
],
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -20,9 +20,9 @@
|
|||
"addPrefixTest",
|
||||
"auth"
|
||||
],
|
||||
"name": "myprovider@test",
|
||||
"provider": "myprovider@test",
|
||||
"name": "test@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar.other`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
||||
]
|
8
pkg/api/testdata/service-bar.json
vendored
8
pkg/api/testdata/service-bar.json
vendored
|
@ -7,13 +7,13 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.1": "UP"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@foo",
|
||||
"myprovider@test"
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
6
pkg/api/testdata/services-page2.json
vendored
6
pkg/api/testdata/services-page2.json
vendored
|
@ -8,13 +8,13 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.2": "UP"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@foo"
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
14
pkg/api/testdata/services.json
vendored
14
pkg/api/testdata/services.json
vendored
|
@ -8,14 +8,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.1": "UP"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@foo",
|
||||
"myprovider@test"
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -27,13 +27,13 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.2": "UP"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider@foo"
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
6
pkg/api/testdata/tcprouter-bar.json
vendored
6
pkg/api/testdata/tcprouter-bar.json
vendored
|
@ -2,8 +2,8 @@
|
|||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
6
pkg/api/testdata/tcprouters-page2.json
vendored
6
pkg/api/testdata/tcprouters-page2.json
vendored
|
@ -3,9 +3,9 @@
|
|||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`toto.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
}
|
||||
]
|
12
pkg/api/testdata/tcprouters.json
vendored
12
pkg/api/testdata/tcprouters.json
vendored
|
@ -3,19 +3,19 @@
|
|||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "myprovider@foo-service"
|
||||
"service": "foo-service@myprovider"
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "myprovider@test",
|
||||
"provider": "myprovider@test",
|
||||
"name": "test@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar.other`)",
|
||||
"service": "myprovider@foo-service",
|
||||
"service": "foo-service@myprovider",
|
||||
"tls": {
|
||||
"passthrough": false
|
||||
}
|
||||
|
|
8
pkg/api/testdata/tcpservice-bar.json
vendored
8
pkg/api/testdata/tcpservice-bar.json
vendored
|
@ -6,10 +6,10 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@foo",
|
||||
"myprovider@test"
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
6
pkg/api/testdata/tcpservices-page2.json
vendored
6
pkg/api/testdata/tcpservices-page2.json
vendored
|
@ -7,10 +7,10 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@foo"
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
14
pkg/api/testdata/tcpservices.json
vendored
14
pkg/api/testdata/tcpservices.json
vendored
|
@ -7,11 +7,11 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@bar",
|
||||
"provider": "myprovider@bar",
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@foo",
|
||||
"myprovider@test"
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -22,10 +22,10 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"name": "myprovider@baz",
|
||||
"provider": "myprovider@baz",
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"usedBy": [
|
||||
"myprovider@foo"
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
|
@ -128,5 +128,5 @@ func contains(cmds []*Command, name string) bool {
|
|||
}
|
||||
|
||||
func isFlag(arg string) bool {
|
||||
return len(arg) > 0 && arg[1] == '-'
|
||||
return len(arg) > 0 && arg[0] == '-'
|
||||
}
|
||||
|
|
|
@ -14,24 +14,14 @@ type EnvLoader struct{}
|
|||
|
||||
// Load loads the command's configuration from the environment variables.
|
||||
func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
|
||||
return e.load(os.Environ(), cmd)
|
||||
}
|
||||
|
||||
func (*EnvLoader) load(environ []string, cmd *Command) (bool, error) {
|
||||
var found bool
|
||||
for _, value := range environ {
|
||||
if strings.HasPrefix(value, "TRAEFIK_") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, cmd.Configuration)
|
||||
if len(vars) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := env.Decode(environ, cmd.Configuration); err != nil {
|
||||
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err)
|
||||
if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
|
||||
log.WithoutContext().Debug("environment variables", strings.Join(vars, ", "))
|
||||
return false, fmt.Errorf("failed to decode configuration from environment variables: %v ", err)
|
||||
}
|
||||
|
||||
log.WithoutContext().Println("Configuration loaded from environment variables.")
|
||||
|
|
|
@ -12,6 +12,10 @@ type FlagLoader struct{}
|
|||
|
||||
// Load loads the command's configuration from flag arguments.
|
||||
func (*FlagLoader) Load(args []string, cmd *Command) (bool, error) {
|
||||
if len(args) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := flag.Decode(args, cmd.Configuration); err != nil {
|
||||
return false, fmt.Errorf("failed to decode configuration from flags: %v", err)
|
||||
}
|
||||
|
|
32
pkg/config/env/env.go
vendored
32
pkg/config/env/env.go
vendored
|
@ -2,28 +2,38 @@
|
|||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
// DefaultNamePrefix is the default prefix for environment variable names.
|
||||
const DefaultNamePrefix = "TRAEFIK_"
|
||||
|
||||
// Decode decodes the given environment variables into the given element.
|
||||
// The operation goes through four stages roughly summarized as:
|
||||
// env vars -> map
|
||||
// map -> tree of untyped nodes
|
||||
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
||||
// "typed" nodes -> typed element
|
||||
func Decode(environ []string, element interface{}) error {
|
||||
func Decode(environ []string, prefix string, element interface{}) error {
|
||||
if err := checkPrefix(prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vars := make(map[string]string)
|
||||
for _, evr := range environ {
|
||||
n := strings.SplitN(evr, "=", 2)
|
||||
if strings.HasPrefix(strings.ToUpper(n[0]), "TRAEFIK_") {
|
||||
if strings.HasPrefix(strings.ToUpper(n[0]), prefix) {
|
||||
key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
|
||||
vars[key] = n[1]
|
||||
}
|
||||
}
|
||||
|
||||
return parser.Decode(vars, element)
|
||||
rootName := strings.ToLower(prefix[:len(prefix)-1])
|
||||
return parser.Decode(vars, element, rootName)
|
||||
}
|
||||
|
||||
// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
|
||||
|
@ -36,7 +46,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
node, err := parser.EncodeToNode(element, false)
|
||||
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -48,3 +58,17 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
|||
|
||||
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"})
|
||||
}
|
||||
|
||||
func checkPrefix(prefix string) error {
|
||||
prefixPattern := `[a-zA-Z0-9]+_`
|
||||
matched, err := regexp.MatchString(prefixPattern, prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return fmt.Errorf("invalid prefix %q, the prefix pattern must match the following pattern: %s", prefix, prefixPattern)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
38
pkg/config/env/env_test.go
vendored
38
pkg/config/env/env_test.go
vendored
|
@ -173,7 +173,7 @@ func TestDecode(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := Decode(test.environ, test.element)
|
||||
err := Decode(test.environ, DefaultNamePrefix, test.element)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, test.element)
|
||||
|
@ -460,39 +460,3 @@ func TestEncode(t *testing.T) {
|
|||
|
||||
assert.Equal(t, expected, flats)
|
||||
}
|
||||
|
||||
type Ya struct {
|
||||
Foo *Yaa
|
||||
Field1 string
|
||||
Field2 bool
|
||||
Field3 int
|
||||
Field4 map[string]string
|
||||
Field5 map[string]int
|
||||
Field6 map[string]struct{ Field string }
|
||||
Field7 map[string]struct{ Field map[string]string }
|
||||
Field8 map[string]*struct{ Field string }
|
||||
Field9 map[string]*struct{ Field map[string]string }
|
||||
Field10 struct{ Field string }
|
||||
Field11 *struct{ Field string }
|
||||
Field12 *string
|
||||
Field13 *bool
|
||||
Field14 *int
|
||||
Field15 []int
|
||||
}
|
||||
|
||||
type Yaa struct {
|
||||
FieldIn1 string
|
||||
FieldIn2 bool
|
||||
FieldIn3 int
|
||||
FieldIn4 map[string]string
|
||||
FieldIn5 map[string]int
|
||||
FieldIn6 map[string]struct{ Field string }
|
||||
FieldIn7 map[string]struct{ Field map[string]string }
|
||||
FieldIn8 map[string]*struct{ Field string }
|
||||
FieldIn9 map[string]*struct{ Field map[string]string }
|
||||
FieldIn10 struct{ Field string }
|
||||
FieldIn11 *struct{ Field string }
|
||||
FieldIn12 *string
|
||||
FieldIn13 *bool
|
||||
FieldIn14 *int
|
||||
}
|
||||
|
|
64
pkg/config/env/filter.go
vendored
Normal file
64
pkg/config/env/filter.go
vendored
Normal 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
87
pkg/config/env/filter_test.go
vendored
Normal 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
69
pkg/config/env/fixtures_test.go
vendored
Normal 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
|
||||
}
|
|
@ -36,13 +36,13 @@ func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return decodeRawToNode(data, filters...)
|
||||
return decodeRawToNode(data, parser.DefaultRootName, filters...)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
|
||||
}
|
||||
|
||||
return decodeRawToNode(data, filters...)
|
||||
return decodeRawToNode(data, parser.DefaultRootName, filters...)
|
||||
}
|
||||
|
||||
func getRootFieldNames(element interface{}) []string {
|
||||
|
|
|
@ -248,7 +248,6 @@ func Test_decodeFileToNode_Toml(t *testing.T) {
|
|||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
||||
{Name: "ForceTaskHostname", Value: "true"},
|
||||
{Name: "KeepAlive", Value: "42"},
|
||||
{Name: "RespectReadinessChecks", Value: "true"},
|
||||
|
@ -518,7 +517,6 @@ func Test_decodeFileToNode_Yaml(t *testing.T) {
|
|||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", Value: "true"},
|
||||
{Name: "ForceTaskHostname", Value: "true"},
|
||||
{Name: "KeepAlive", Value: "42"},
|
||||
{Name: "RespectReadinessChecks", Value: "true"},
|
||||
|
|
|
@ -74,7 +74,6 @@
|
|||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
DCOSToken = "foobar"
|
||||
FilterMarathonConstraints = true
|
||||
DialerTimeout = 42
|
||||
ResponseHeaderTimeout = 42
|
||||
TLSHandshakeTimeout = 42
|
||||
|
|
|
@ -69,7 +69,6 @@ Providers:
|
|||
DefaultRule: foobar
|
||||
ExposedByDefault: true
|
||||
DCOSToken: foobar
|
||||
FilterMarathonConstraints: true
|
||||
DialerTimeout: 42
|
||||
ResponseHeaderTimeout: 42
|
||||
TLSHandshakeTimeout: 42
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
func decodeRawToNode(data map[string]interface{}, filters ...string) (*parser.Node, error) {
|
||||
func decodeRawToNode(data map[string]interface{}, rootName string, filters ...string) (*parser.Node, error) {
|
||||
root := &parser.Node{
|
||||
Name: "traefik",
|
||||
Name: rootName,
|
||||
}
|
||||
|
||||
vData := reflect.ValueOf(data)
|
||||
|
|
|
@ -531,7 +531,7 @@ func Test_decodeRawToNode(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, err := decodeRawToNode(test.data)
|
||||
node, err := decodeRawToNode(test.data, parser.DefaultRootName)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, node)
|
||||
|
|
|
@ -17,7 +17,7 @@ func Decode(args []string, element interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return parser.Decode(ref, element)
|
||||
return parser.Decode(ref, element, parser.DefaultRootName)
|
||||
}
|
||||
|
||||
// Encode encodes the configuration in element into the flags represented in the returned Flats.
|
||||
|
@ -30,7 +30,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
node, err := parser.EncodeToNode(element, false)
|
||||
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
// Parse parses the command-line flag arguments into a map,
|
||||
|
@ -96,7 +98,7 @@ func (f *flagSet) parseOne() (bool, error) {
|
|||
}
|
||||
|
||||
func (f *flagSet) setValue(name string, value string) {
|
||||
n := strings.ToLower("traefik." + name)
|
||||
n := strings.ToLower(parser.DefaultRootName + "." + name)
|
||||
v, ok := f.values[n]
|
||||
|
||||
if ok && f.flagTypes[name] == reflect.Slice {
|
||||
|
|
|
@ -13,7 +13,7 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
|
|||
TCP: &config.TCPConfiguration{},
|
||||
}
|
||||
|
||||
err := parser.Decode(labels, conf, "traefik.http", "traefik.tcp")
|
||||
err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
|
|||
|
||||
// EncodeConfiguration converts a configuration to labels.
|
||||
func EncodeConfiguration(conf *config.Configuration) (map[string]string, error) {
|
||||
return parser.Encode(conf)
|
||||
return parser.Encode(conf, parser.DefaultRootName)
|
||||
}
|
||||
|
||||
// Decode converts the labels to an element.
|
||||
// labels -> [ node -> node + metadata (type) ] -> element (node)
|
||||
func Decode(labels map[string]string, element interface{}, filters ...string) error {
|
||||
return parser.Decode(labels, element, filters...)
|
||||
return parser.Decode(labels, element, parser.DefaultRootName, filters...)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
|
||||
// EncodeToNode converts an element to a node.
|
||||
// element -> nodes
|
||||
func EncodeToNode(element interface{}, omitEmpty bool) (*Node, error) {
|
||||
func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node, error) {
|
||||
rValue := reflect.ValueOf(element)
|
||||
node := &Node{Name: "traefik"}
|
||||
node := &Node{Name: rootName}
|
||||
|
||||
encoder := encoderToNode{omitEmpty: omitEmpty}
|
||||
|
||||
|
|
|
@ -723,7 +723,7 @@ func TestEncodeToNode(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, err := EncodeToNode(test.element, true)
|
||||
node, err := EncodeToNode(test.element, DefaultRootName, true)
|
||||
|
||||
if test.expected.error {
|
||||
require.Error(t, err)
|
||||
|
|
|
@ -6,18 +6,16 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const labelRoot = "traefik"
|
||||
|
||||
// DecodeToNode converts the labels to a tree of nodes.
|
||||
// If any filters are present, labels which do not match the filters are skipped.
|
||||
func DecodeToNode(labels map[string]string, filters ...string) (*Node, error) {
|
||||
func DecodeToNode(labels map[string]string, rootName string, filters ...string) (*Node, error) {
|
||||
sortedKeys := sortKeys(labels, filters)
|
||||
|
||||
var node *Node
|
||||
for i, key := range sortedKeys {
|
||||
split := strings.Split(key, ".")
|
||||
|
||||
if split[0] != labelRoot {
|
||||
if split[0] != rootName {
|
||||
return nil, fmt.Errorf("invalid label root %s", split[0])
|
||||
}
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ func TestDecodeToNode(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
out, err := DecodeToNode(test.in, test.filters...)
|
||||
out, err := DecodeToNode(test.in, DefaultRootName, test.filters...)
|
||||
|
||||
if test.expected.error {
|
||||
require.Error(t, err)
|
||||
|
|
|
@ -2,6 +2,9 @@ package parser
|
|||
|
||||
import "reflect"
|
||||
|
||||
// DefaultRootName is the default name of the root node and the prefix of element name from the resources.
|
||||
const DefaultRootName = "traefik"
|
||||
|
||||
// MapNamePlaceholder is the placeholder for the map name.
|
||||
const MapNamePlaceholder = "<name>"
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ package parser
|
|||
// labels -> tree of untyped nodes
|
||||
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
||||
// "typed" nodes -> typed element
|
||||
func Decode(labels map[string]string, element interface{}, filters ...string) error {
|
||||
node, err := DecodeToNode(labels, filters...)
|
||||
func Decode(labels map[string]string, element interface{}, rootName string, filters ...string) error {
|
||||
node, err := DecodeToNode(labels, rootName, filters...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ func Decode(labels map[string]string, element interface{}, filters ...string) er
|
|||
|
||||
// Encode converts an element to labels.
|
||||
// element -> node (value) -> label (node)
|
||||
func Encode(element interface{}) (map[string]string, error) {
|
||||
node, err := EncodeToNode(element, true)
|
||||
func Encode(element interface{}, rootName string) (map[string]string, error) {
|
||||
node, err := EncodeToNode(element, rootName, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ type TCPServiceInfo struct {
|
|||
func getProviderName(elementName string) string {
|
||||
parts := strings.Split(elementName, "@")
|
||||
if len(parts) > 1 {
|
||||
return parts[0]
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ func getProviderName(elementName string) string {
|
|||
func getQualifiedName(provider, elementName string) string {
|
||||
parts := strings.Split(elementName, "@")
|
||||
if len(parts) == 1 {
|
||||
return provider + "@" + elementName
|
||||
return elementName + "@" + provider
|
||||
}
|
||||
return elementName
|
||||
}
|
||||
|
|
|
@ -25,23 +25,23 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "One service used by two routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -59,12 +59,12 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@foo": {},
|
||||
"myprovider@bar": {},
|
||||
"foo@myprovider": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -73,7 +73,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "One service used by two routers, but one router with wrong rule",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -84,17 +84,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -102,12 +102,12 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@foo": {},
|
||||
"myprovider@bar": {},
|
||||
"foo@myprovider": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -116,17 +116,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "Broken Service used by one Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -134,11 +134,11 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -147,7 +147,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "2 different Services each used by a disctinct router.",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -165,7 +165,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"myprovider@bar-service": {
|
||||
"bar-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -185,17 +185,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -203,15 +203,15 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"myprovider@foo": {},
|
||||
"bar@myprovider": {},
|
||||
"foo@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
"myprovider@bar-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"bar-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -220,7 +220,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "2 middlewares both used by 2 Routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -233,14 +233,14 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
|
@ -249,18 +249,18 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest"},
|
||||
},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
|
@ -269,20 +269,20 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"myprovider@test": {},
|
||||
"bar@myprovider": {},
|
||||
"test@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
"auth@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
"addPrefixTest@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -291,7 +291,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "Unknown middleware is not used by the Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -304,7 +304,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
|
@ -313,10 +313,10 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"unknown"},
|
||||
},
|
||||
|
@ -325,8 +325,8 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -335,7 +335,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "Broken middleware is used by Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -348,7 +348,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"badConf"},
|
||||
|
@ -357,28 +357,28 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"myprovider@auth"},
|
||||
Middlewares: []string{"auth@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"auth@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -387,7 +387,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "2 middlewares from 2 disctinct providers both used by 2 Routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
|
@ -400,21 +400,21 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
"auth@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
|
@ -423,18 +423,18 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider@addPrefixTest"},
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
},
|
||||
"myprovider@test": {
|
||||
"test@myprovider": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
|
@ -443,23 +443,23 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"myprovider@test": {},
|
||||
"bar@myprovider": {},
|
||||
"test@myprovider": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider@auth": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@test"},
|
||||
"auth@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
},
|
||||
"myprovider@addPrefixTest": {
|
||||
UsedBy: []string{"myprovider@test"},
|
||||
"addPrefixTest@myprovider": {
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
},
|
||||
"anotherprovider@addPrefixTest": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"addPrefixTest@anotherprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -470,23 +470,23 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "TCP, One service used by two routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -506,12 +506,12 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@foo": {},
|
||||
"myprovider@bar": {},
|
||||
"foo@myprovider": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -520,7 +520,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "TCP, One service used by two routers, but one router with wrong rule",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -533,17 +533,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -551,12 +551,12 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@foo": {},
|
||||
"myprovider@bar": {},
|
||||
"foo@myprovider": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar", "myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider", "foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -565,17 +565,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "TCP, Broken Service used by one Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -583,11 +583,11 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"bar@myprovider": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -596,7 +596,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
desc: "TCP, 2 different Services each used by a disctinct router.",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
"foo-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -612,7 +612,7 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"myprovider@bar-service": {
|
||||
"bar-service@myprovider": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
|
@ -630,17 +630,17 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@foo": {
|
||||
"foo@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider@bar": {
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
|
@ -648,15 +648,15 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider@bar": {},
|
||||
"myprovider@foo": {},
|
||||
"bar@myprovider": {},
|
||||
"foo@myprovider": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider@foo-service": {
|
||||
UsedBy: []string{"myprovider@foo"},
|
||||
"foo-service@myprovider": {
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
"myprovider@bar-service": {
|
||||
UsedBy: []string{"myprovider@bar"},
|
||||
"bar-service@myprovider": {
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -716,7 +716,7 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
@ -725,7 +725,7 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
@ -741,17 +741,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -760,17 +760,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -782,14 +782,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -803,17 +803,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -822,17 +822,17 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -844,14 +844,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -861,14 +861,14 @@ func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
|||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -914,7 +914,7 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
@ -923,7 +923,7 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
@ -939,17 +939,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -958,17 +958,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -980,14 +980,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
"foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -1001,17 +1001,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -1020,17 +1020,17 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -1042,14 +1042,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
"foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider@foo-service",
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
@ -1059,14 +1059,14 @@ func TestGetRoutersByEntrypoints(t *testing.T) {
|
|||
Router: &config.Router{
|
||||
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider@bar-service",
|
||||
Service: "bar-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider@foobar-service",
|
||||
Service: "foobar-service@myprovider",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -55,7 +55,7 @@ type Configuration struct {
|
|||
Ping *ping.Handler `description:"Enable ping." export:"true" label:"allowEmpty"`
|
||||
// Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
|
||||
|
||||
Log *types.TraefikLog `description:"Traefik log settings." export:"true"`
|
||||
Log *types.TraefikLog `description:"Traefik log settings." export:"true" label:"allowEmpty"`
|
||||
AccessLog *types.AccessLog `description:"Access log settings." export:"true" label:"allowEmpty"`
|
||||
Tracing *Tracing `description:"OpenTracing configuration." export:"true" label:"allowEmpty"`
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package provider
|
||||
|
||||
import "github.com/containous/traefik/pkg/types"
|
||||
|
||||
// Constrainer Filter services by constraint, matching with Traefik tags.
|
||||
type Constrainer struct {
|
||||
Constraints []*types.Constraint `description:"Filter services by constraint, matching with Traefik tags." export:"true"`
|
||||
}
|
||||
|
||||
// MatchConstraints must match with EVERY single constraint
|
||||
// returns first constraint that do not match or nil.
|
||||
func (c *Constrainer) MatchConstraints(tags []string) (bool, *types.Constraint) {
|
||||
// if there is no tags and no constraints, filtering is disabled
|
||||
if len(tags) == 0 && len(c.Constraints) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, constraint := range c.Constraints {
|
||||
// xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint
|
||||
if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); ok != constraint.MustMatch {
|
||||
return false, constraint
|
||||
}
|
||||
}
|
||||
|
||||
// If no constraint or every constraints matching
|
||||
return true, nil
|
||||
}
|
100
pkg/provider/constraints/constraints.go
Normal file
100
pkg/provider/constraints/constraints.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/vulcand/predicate"
|
||||
)
|
||||
|
||||
// MarathonConstraintPrefix is the prefix for each label's key created from a Marathon application constraint.
|
||||
// It is used in order to create a specific and unique pattern for these labels.
|
||||
const MarathonConstraintPrefix = "Traefik-Marathon-505F9E15-BDC7-45E7-828D-C06C7BAB8091"
|
||||
|
||||
type constraintFunc func(map[string]string) bool
|
||||
|
||||
// Match reports whether the expression matches with the given labels.
|
||||
// The expression must match any logical boolean combination of:
|
||||
// - `Label(labelName, labelValue)`
|
||||
// - `LabelRegex(labelName, regexValue)`
|
||||
// - `MarathonConstraint(field:operator:value)`
|
||||
func Match(labels map[string]string, expr string) (bool, error) {
|
||||
if expr == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
p, err := predicate.NewParser(predicate.Def{
|
||||
Operators: predicate.Operators{
|
||||
AND: andFunc,
|
||||
NOT: notFunc,
|
||||
OR: orFunc,
|
||||
},
|
||||
Functions: map[string]interface{}{
|
||||
"Label": labelFn,
|
||||
"LabelRegex": labelRegexFn,
|
||||
"MarathonConstraint": marathonFn,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
parse, err := p.Parse(expr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
fn, ok := parse.(constraintFunc)
|
||||
if !ok {
|
||||
return false, errors.New("not a constraintFunc")
|
||||
}
|
||||
return fn(labels), nil
|
||||
}
|
||||
|
||||
func labelFn(name, value string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return labels[name] == value
|
||||
}
|
||||
}
|
||||
|
||||
func labelRegexFn(name, expr string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
matched, err := regexp.MatchString(expr, labels[name])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return matched
|
||||
}
|
||||
}
|
||||
|
||||
func marathonFn(value string) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
for k, v := range labels {
|
||||
if strings.HasPrefix(k, MarathonConstraintPrefix) {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func andFunc(a, b constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return a(labels) && b(labels)
|
||||
}
|
||||
}
|
||||
|
||||
func orFunc(a, b constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return a(labels) || b(labels)
|
||||
}
|
||||
}
|
||||
|
||||
func notFunc(a constraintFunc) constraintFunc {
|
||||
return func(labels map[string]string) bool {
|
||||
return !a(labels)
|
||||
}
|
||||
}
|
204
pkg/provider/constraints/constraints_test.go
Normal file
204
pkg/provider/constraints/constraints_test.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
expr string
|
||||
labels map[string]string
|
||||
expected bool
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
expr: `Label("hello", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hi", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `!Label("hello", "world")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds") && Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && !Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") || Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "worlds") || Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") || !Label("foo", "bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
expr: `Foo("hello")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "bar")`,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: ``,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `MarathonConstraint("bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `MarathonConstraint("bur")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `Label("hello", "world") && MarathonConstraint("bar")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
MarathonConstraintPrefix + "-1": "bar",
|
||||
MarathonConstraintPrefix + "-2": "foo",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w\\w+s")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hi", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `!LabelRegex("hello", "w\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
expr: `LabelRegex("hello", "w(\\w+")`,
|
||||
labels: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.expr, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
matches, err := Match(test.labels, test.expr)
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, test.expected, matches)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/containous/traefik/pkg/config/label"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/provider"
|
||||
"github.com/containous/traefik/pkg/provider/constraints"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
|
@ -123,10 +124,13 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
|
|||
return false
|
||||
}
|
||||
|
||||
if ok, failingConstraint := p.MatchConstraints(container.ExtraConf.Tags); !ok {
|
||||
if failingConstraint != nil {
|
||||
logger.Debugf("Container pruned by %q constraint", failingConstraint.String())
|
||||
}
|
||||
matches, err := constraints.Match(container.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Error("Error matching constraints expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
docker "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/go-connections/nat"
|
||||
|
@ -339,7 +338,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
containers []dockerData
|
||||
constraints []*types.Constraint
|
||||
constraints string
|
||||
expected *config.Configuration
|
||||
}{
|
||||
{
|
||||
|
@ -1924,13 +1923,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "bar")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
@ -1965,13 +1958,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
constraints: []*types.Constraint{
|
||||
{
|
||||
Key: "tag",
|
||||
MustMatch: true,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
constraints: `Label("traefik.tags", "foo")`,
|
||||
expected: &config.Configuration{
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{},
|
||||
|
|
|
@ -45,7 +45,7 @@ var _ provider.Provider = (*Provider)(nil)
|
|||
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
provider.Constrainer `description:"List of constraints used to filter out some containers." export:"true"`
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." export:"true"`
|
||||
Watch bool `description:"Watch provider." export:"true"`
|
||||
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint."`
|
||||
DefaultRule string `description:"Default rule."`
|
||||
|
|
|
@ -14,7 +14,6 @@ const (
|
|||
// configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider.
|
||||
type configuration struct {
|
||||
Enable bool
|
||||
Tags []string
|
||||
Docker specificConfiguration
|
||||
}
|
||||
|
||||
|
@ -31,7 +30,7 @@ func (p *Provider) getConfiguration(container dockerData) (configuration, error)
|
|||
},
|
||||
}
|
||||
|
||||
err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable", "traefik.tags")
|
||||
err := label.Decode(container.Labels, &conf, "traefik.docker.", "traefik.enable")
|
||||
if err != nil {
|
||||
return configuration{}, err
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ type Client interface {
|
|||
GetIngressRoutes() []*v1alpha1.IngressRoute
|
||||
GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP
|
||||
GetMiddlewares() []*v1alpha1.Middleware
|
||||
GetTLSOptions() []*v1alpha1.TLSOption
|
||||
|
||||
GetIngresses() []*extensionsv1beta1.Ingress
|
||||
GetService(namespace, name string) (*corev1.Service, bool, error)
|
||||
|
@ -158,6 +159,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
|||
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
|
||||
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
|
||||
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
|
||||
factoryCrd.Traefik().V1alpha1().TLSOptions().Informer().AddEventHandler(eventHandler)
|
||||
|
||||
factoryKube := informers.NewFilteredSharedInformerFactory(c.csKube, resyncPeriod, ns, nil)
|
||||
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
||||
|
@ -241,6 +243,21 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
|
|||
return result
|
||||
}
|
||||
|
||||
// GetTLSOptions
|
||||
func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
|
||||
var result []*v1alpha1.TLSOption
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(c.labelSelector)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list tls options in namespace %s: %s", ns, err)
|
||||
}
|
||||
result = append(result, options...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
|
||||
func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||
var result []*extensionsv1beta1.Ingress
|
||||
|
|
|
@ -37,6 +37,7 @@ type clientMock struct {
|
|||
ingressRoutes []*v1alpha1.IngressRoute
|
||||
ingressRouteTCPs []*v1alpha1.IngressRouteTCP
|
||||
middlewares []*v1alpha1.Middleware
|
||||
tlsOptions []*v1alpha1.TLSOption
|
||||
|
||||
watchChan chan interface{}
|
||||
}
|
||||
|
@ -63,6 +64,8 @@ func newClientMock(paths ...string) clientMock {
|
|||
c.ingressRouteTCPs = append(c.ingressRouteTCPs, o)
|
||||
case *v1alpha1.Middleware:
|
||||
c.middlewares = append(c.middlewares, o)
|
||||
case *v1alpha1.TLSOption:
|
||||
c.tlsOptions = append(c.tlsOptions, o)
|
||||
case *v1beta12.Ingress:
|
||||
c.ingresses = append(c.ingresses, o)
|
||||
case *corev1.Secret:
|
||||
|
@ -88,6 +91,20 @@ func (c clientMock) GetMiddlewares() []*v1alpha1.Middleware {
|
|||
return c.middlewares
|
||||
}
|
||||
|
||||
func (c clientMock) GetTLSOptions() []*v1alpha1.TLSOption {
|
||||
return c.tlsOptions
|
||||
}
|
||||
|
||||
func (c clientMock) GetTLSOption(namespace, name string) (*v1alpha1.TLSOption, bool, error) {
|
||||
for _, option := range c.tlsOptions {
|
||||
if option.Namespace == namespace && option.Name == name {
|
||||
return option, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||
return c.ingresses
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
Loading…
Reference in a new issue