1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-24 21:44:26 +03:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Ludovic Fernandez
fdb1701d1b Prepare release v2.0.0-alpha7 2019-06-21 17:46:02 +02:00
Jean-Baptiste Doumenjou
80b35575df Define TLS options on the Router configuration for Kubernetes
Co-authored-by: juliens <julien@containo.us>
2019-06-21 17:18:05 +02:00
Julien Salleyron
69cf05df9a Fix panic in tls manager 2019-06-21 16:32:04 +02:00
Ludovic Fernandez
69a1817c3f Improve some parts of the documentation. 2019-06-21 10:54:04 +02:00
Ludovic Fernandez
a918dcd5a4 Filter env vars configuration 2019-06-21 10:08:04 +02:00
Ludovic Fernandez
adc9a65ae3 Use name@provider instead of provider@name. 2019-06-21 09:54:04 +02:00
Ludovic Fernandez
1e779f7135 Fix some CLI bugs 2019-06-21 09:40:04 +02:00
Ludovic Fernandez
fe68e9e243 New constraints management.
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
2019-06-21 09:24:04 +02:00
Ludovic Fernandez
e9792b446f Change the provider separator from . to @ 2019-06-20 00:40:05 +02:00
mpl
4012599264 docs: rewrite of the HTTPS and TLS section
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2019-06-19 19:00:06 +02:00
mpl
429b1d8574 API: new contract
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2019-06-19 18:34:04 +02:00
Ludovic Fernandez
a34876d700 Display an error when help is called on a non existing command. 2019-06-19 14:08:05 +02:00
Michael
68ecf78f0e Upgrade k3s version 2019-06-18 22:32:05 +02:00
Ludovic Fernandez
38344b342d Prepare release v2.0.0-alpha6. 2019-06-18 18:10:06 +02:00
Orhan Hirsch
346ff96de2 Kubernetes CRD documentation fixes 2019-06-18 12:20:04 +02:00
Ludovic Fernandez
31614bebc4 Don't allow non flag arguments by default. 2019-06-18 12:10:06 +02:00
Ludovic Fernandez
be888b59a6 doc: fix middleware names for CRD. 2019-06-18 09:50:05 +02:00
168 changed files with 5856 additions and 1362 deletions

View File

@@ -1,5 +1,35 @@
# 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)
**Bug fixes:**
- **[cli]** Don&#39;t allow non flag arguments by default. ([#4970](https://github.com/containous/traefik/pull/4970) by [ldez](https://github.com/ldez))
**Documentation:**
- **[middleware,k8s/crd]** doc: fix middleware names for CRD. ([#4966](https://github.com/containous/traefik/pull/4966) by [ldez](https://github.com/ldez))
- **[middleware]** Kubernetes CRD documentation fixes ([#4971](https://github.com/containous/traefik/pull/4971) by [orhanhenrik](https://github.com/orhanhenrik))
## [v2.0.0-alpha5](https://github.com/containous/traefik/tree/v2.0.0-alpha5) (2019-06-17)
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha4...v2.0.0-alpha5)

15
Gopkg.lock generated
View File

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

View File

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

View File

@@ -204,16 +204,11 @@ func configureLogging(staticConfiguration *static.Configuration) {
// an explicitly defined log level always has precedence. if none is
// given and debug mode is disabled, the default is ERROR, and DEBUG
// otherwise.
var levelStr string
if staticConfiguration.Log != nil {
levelStr := "error"
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
levelStr = strings.ToLower(staticConfiguration.Log.Level)
}
if levelStr == "" {
levelStr = "error"
if staticConfiguration.Global.Debug {
levelStr = "debug"
}
}
level, err := logrus.ParseLevel(levelStr)
if err != nil {
log.WithoutContext().Errorf("Error getting level: %v", err)

View File

@@ -10,10 +10,10 @@ Configuration in Traefik can refer to two different things:
- The fully dynamic routing configuration (referred to as the _dynamic configuration_)
- The startup configuration (referred to as the _static configuration_)
Elements in the _static configuration_ set up connections to [providers](../../providers/overview/) and define the [entrypoints](../../routing/entrypoints/) Traefik will listen to (these elements don't change often).
Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
This configuration can change and is seamlessly hot-reloaded, without any request interuption or connection loss.
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
!!! warning "Incompatible Configuration"
Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now.
@@ -70,6 +70,12 @@ docker run traefik[:version] --help
# ex: docker run traefik:2.0 --help
```
All available arguments can also be found [here](../reference/static-configuration/cli.md).
### Environment Variables
All available environment variables can be found [here](../reference/static-configuration/env.md)
## Available Configuration Options
All the configuration options are documented in their related section.

View File

@@ -1,145 +0,0 @@
# HTTPS & TLS
Traefik supports HTTPS & TLS, and is able to accept new certificates / updates over time (without being restarted).
TLS is enabled at the [router](../routing/routers/index.md) level, but some options are configured in dedicated sections (`tlsOptions` & `tlsStores`) described in this section.
## Configuration Example
??? example "Configuring a Default Certificate"
```toml
[tlsStores]
[tlsStores.default]
[tlsStores.default.defaultCertificate]
certFile = "path/to/cert.crt"
keyFile = "path/to/cert.key"
```
??? example "Configuring a Minimum TLS Version"
```toml
[tlsOptions]
[tlsOptions.default]
minVersion = "VersionTLS12"
```
??? example "Defining Certificates"
```toml
[[tls]]
[tls.certificate]
certFile = "/path/to/domain.cert"
keyFile = "/path/to/domain.key"
[[tls]]
[tls.certificate]
certFile = "/path/to/other-domain.cert"
keyFile = "/path/to/other-domain.key"
```
!!! important "File Provider Only"
In the above example, we've used the [file provider](../providers/file.md) to handle the TLS configuration (tlsStores, tlsOptions, and TLS certificates).
In its current alpha version, it is the only available method to configure these elements.
Of course, these options are hot reloaded and can be updated at runtime (they belong to the [dynamic configuration](../getting-started/configuration-overview.md)).
## Configuration Options
### Dynamic Certificates
To add / remove TLS certificates while Traefik is running, the [file provider](../providers/file.md) supports Dynamic TLS certificates in its `[[tls]]` section.
!!! example "Defining Certificates"
```toml
[[tls]]
stores = ["default"]
[tls.certificate]
certFile = "/path/to/domain.cert"
keyFile = "/path/to/domain.key"
[[tls]]
stores = ["default"]
[tls.certificate]
certFile = "/path/to/other-domain.cert"
keyFile = "/path/to/other-domain.key"
```
??? note "Stores"
During the alpha version, the stores option will be ignored and be automatically set to ["default"].
### Mutual Authentication
Traefik supports both optional and non optional (defaut value) mutual authentication.
- When `optional = false`, Traefik accepts connections only from client presenting a certificate signed by a CA listed in `ClientCA.files`.
- When `optional = true`, Traefik authorizes connections from client presenting a certificate signed by an unknown CA.
!!! example "Non Optional Mutual Authentication"
In the following example, both `snitest.com` and `snitest.org` will require client certificates.
```toml
[tlsOptions]
[tlsOptions.default]
[tlsOptions.default.ClientCA]
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
optional = false
```
??? note "ClientCA.files"
You can use a file per `CA:s`, or a single file containing multiple `CA:s` (in `PEM` format).
`ClientCA.files` is not optional: every client will have to present a valid certificate. (This requirement will apply to every server certificate declared in the entrypoint.)
### Minimum TLS Version
!!! example "Min TLS version & [cipherSuites](https://godoc.org/crypto/tls#pkg-constants)"
```toml
[tlsOptions]
[tlsOptions.default]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
```
### Strict SNI Checking
With strict SNI checking, Traefik won't allow connections without a matching certificate.
!!! example "Strict SNI"
```toml
[tlsOptions]
[tlsOptions.default]
sniStrict = true
```
### Default Certificate
Traefik can use a default certificate for connections without a SNI, or without a matching domain.
If no default certificate is provided, Traefik generates and uses a self-signed certificate.
!!! example "Setting a Default Certificate"
```toml
[tlsStores]
[tlsStores.default]
[tlsStores.default.defaultCertificate]
certFile = "path/to/cert.crt"
keyFile = "path/to/cert.key"
```
??? note "Only One Default Certificate"
There can only be one `defaultCertificate` per tlsOptions.
??? note "Default TLS Store"
During the alpha version, there is only one globally available TLS Store (`default`).

View File

@@ -1,4 +1,4 @@
# ACME
# Let's Encrypt
Automatic HTTPS
{: .subtitle }
@@ -54,7 +54,7 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom
There are many available options for ACME. For a quick glance at what's possible, browse the configuration reference:
```toml
--8<-- "content/https-tls/ref-acme.toml"
--8<-- "content/https/ref-acme.toml"
```
## Automatic Renewals

View File

@@ -0,0 +1,16 @@
# HTTPS & TLS
Overview
{: .subtitle }
Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration:
routers, and the TLS connection (and its underlying certificates).
When a router has to handle HTTPS traffic,
it should be specified with a `tls` field of the router definition.
See the TLS section of the [routers documentation](../routing/routers/index.md#tls).
The next sections of this documentation explain how to configure the TLS connection itself.
That is to say, how to obtain [TLS certificates](./tls.md#certificates-definition):
either through a definition in the dynamic configuration, or through [Let's Encrypt](./acme.md) (ACME).
And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores).

140
docs/content/https/tls.md Normal file
View File

@@ -0,0 +1,140 @@
# TLS
Transport Layer Security
{: .subtitle }
## Certificates Definition
### Automated
See the [Let's Encrypt](./acme.md) page.
### User defined
To add / remove TLS certificates, even when Traefik is already running, their definition can be added to the [dynamic configuration](../getting-started/configuration-overview.md), in the `[[tls]]` section:
```toml
[[tls]]
[tls.certificate]
certFile = "/path/to/domain.cert"
keyFile = "/path/to/domain.key"
[[tls]]
[tls.certificate]
certFile = "/path/to/other-domain.cert"
keyFile = "/path/to/other-domain.key"
```
!!! important "File Provider Only"
In the above example, we've used the [file provider](../providers/file.md) to handle these definitions.
In its current alpha version, it is the only available method to configure the certificates (as well as the options and the stores).
## Certificates Stores
In Traefik, certificates are grouped together in certificates stores, which are defined as such:
```toml
[tlsStores]
[tlsStores.default]
```
!!! important "Alpha restriction"
During the alpha version, any store definition other than the default one (named `default`) will be ignored,
and there is thefore only one globally available TLS store.
In the `[[tls]]` section, a list of stores can then be specified to indicate where the certificates should be stored:
```toml
[[tls]]
stores = ["default"]
[tls.certificate]
certFile = "/path/to/domain.cert"
keyFile = "/path/to/domain.key"
[[tls]]
# Note that since no store is defined,
# the certificate below will be stored in the `default` store.
[tls.certificate]
certFile = "/path/to/other-domain.cert"
keyFile = "/path/to/other-domain.key"
```
!!! important "Alpha restriction"
During the alpha version, the `stores` list will actually be ignored and automatically set to `["default"]`.
### Default Certificate
Traefik can use a default certificate for connections without a SNI, or without a matching domain.
This default certificate should be defined in a TLS store:
```toml
[tlsStores]
[tlsStores.default]
[tlsStores.default.defaultCertificate]
certFile = "path/to/cert.crt"
keyFile = "path/to/cert.key"
```
If no default certificate is provided, Traefik generates and uses a self-signed certificate.
## TLS Options
The TLS options allow one to configure some parameters of the TLS connection.
### Minimum TLS Version
```toml
[tlsOptions]
[tlsOptions.default]
minVersion = "VersionTLS12"
[tlsOptions.mintls13]
minVersion = "VersionTLS13"
```
### Mutual Authentication
Traefik supports both optional and strict (which is the default) mutual authentication, though the `ClientCA.files` section.
If present, connections from clients without a certificate will be rejected.
For clients with a certificate, the `optional` option governs the behaviour as follows:
- When `optional = false`, Traefik accepts connections only from clients presenting a certificate signed by a CA listed in `ClientCA.files`.
- When `optional = true`, Traefik authorizes connections from clients presenting a certificate signed by an unknown CA.
```toml
[tlsOptions]
[tlsOptions.default]
[tlsOptions.default.ClientCA]
# in PEM format. each file can contain multiple CAs.
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
optional = false
```
### Cipher Suites
See [cipherSuites](https://godoc.org/crypto/tls#pkg-constants) for more information.
```toml
[tlsOptions]
[tlsOptions.default]
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
```
### Strict SNI Checking
With strict SNI checking, Traefik won't allow connections from clients connections
that do not specify a server_name extension.
```toml
[tlsOptions]
[tlsOptions.default]
sniStrict = true
```

View File

@@ -15,6 +15,16 @@ labels:
- "traefik.http.middlewares.test-compress.compress=true"
```
```yaml tab="Kubernetes"
# Enable gzip compression
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-compress
spec:
compress: {}
```
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.test-compress.compress": "true"

View File

@@ -19,10 +19,10 @@ labels:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: addprefix
name: test-maxconn
spec:
addPrefix:
prefix: /bar
maxConn:
amount: 10
```
```json tab="Marathon"

View File

@@ -71,6 +71,38 @@ labels:
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
```
```toml tab="File"
[tlsOptions]
[tlsOptions.default]
minVersion = "VersionTLS12"
```
```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"
# As Toml Configuration File
[providers]
@@ -121,8 +153,8 @@ If you use multiple `providers` and wish to reference a middleware declared in a
image: your-docker-image
labels:
# Attach file.add-foo-prefix middleware (declared in file)
- "traefik.http.routers.my-container.middlewares=file.add-foo-prefix"
# Attach add-foo-prefix@file middleware (declared in file)
- "traefik.http.routers.my-container.middlewares=add-foo-prefix@file"
```
## Available Middlewares

View File

@@ -32,15 +32,16 @@ metadata:
name: test-ratelimit
spec:
rateLimit:
extractorfunc = "client.ip"
rate0:
period = "10s"
average = 100
burst = 200
rate1:
period = "3s"
average = 5
burst = 10
extractorFunc: client.ip
rateset:
rate0:
period: 10s
average: 100
burst: 200
rate1:
period: 3s
average: 5
burst: 10
```
```json tab="Marathon"

View File

@@ -22,7 +22,7 @@ kind: Middleware
metadata:
name: test-stripprefix
spec:
StripPrefix:
stripPrefix:
prefixes:
- /foobar
- /fiibar

View File

@@ -1,4 +1,4 @@
# StripPrefix
# StripPrefixRegex
Removing Prefixes From the Path Before Forwarding the Request (Using a Regex)
{: .subtitle }
@@ -22,7 +22,7 @@ kind: Middleware
metadata:
name: test-stripprefixregex
spec:
StripPrefixRegex:
stripPrefixRegex:
regex: "^/foo/(.*)"
```

View File

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

View File

@@ -0,0 +1,168 @@
# API
Traefik exposes a number of information through an API handler, such as the configuration of all routers, services, middlewares, etc.
As with all features of Traefik, this handler can be enabled with the [static configuration](../getting-started/configuration-overview.md#the-static-configuration).
## Security
Enabling the API in production is not recommended, because it will expose all configuration elements,
including sensitive data.
In production, it should be at least secured by authentication and authorizations.
A good sane default (non exhaustive) set of recommendations
would be to apply the following protection mechanisms:
* At the application level:
securing with middlewares such as [basic authentication](../middlewares/basicauth.md) or [white listing](../middlewares/ipwhitelist.md).
* At the transport level:
NOT publicly exposing the API's port,
keeping it restricted to internal networks
(as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks).
## Configuration
To enable the API handler:
```toml tab="File"
[api]
```
```bash tab="CLI"
--api
```
### `dashboard`
_Optional, Default=true_
Enable the dashboard. More about the dashboard features [here](./dashboard.md).
```toml tab="File"
[api]
dashboard = true
```
```bash tab="CLI"
--api.dashboard
```
### `entrypoint`
_Optional, Default="traefik"_
The entry point that the API handler will be bound to.
The default ("traefik") is an internal entry point (which is always defined).
```toml tab="File"
[api]
entrypoint = "web"
```
```bash tab="CLI"
--api.entrypoint="web"
```
### `middlewares`
_Optional, Default=empty_
The list of [middlewares](../middlewares/overview.md) applied to the API handler.
```toml tab="File"
[api]
middlewares = ["api-auth", "api-prefix"]
```
```bash tab="CLI"
--api.middlewares="api-auth,api-prefix"
```
### `debug`
_Optional, Default=false_
Enable additional endpoints for debugging and profiling, served under `/debug/`.
```toml tab="File"
[api]
debug = true
```
```bash tab="CLI"
--api.debug=true
```
## Endpoints
All the following endpoints must be accessed with a `GET` HTTP request.
| Path | Description |
|--------------------------------|-------------------------------------------------------------------------------------------|
| `/api/http/routers` | Lists all the HTTP routers information. |
| `/api/http/routers/{name}` | Returns the information of the HTTP router specified by `name`. |
| `/api/http/services` | Lists all the HTTP services information. |
| `/api/http/services/{name}` | Returns the information of the HTTP service specified by `name`. |
| `/api/http/middlewares` | Lists all the HTTP middlewares information. |
| `/api/http/middlewares/{name}` | Returns the information of the HTTP middleware specified by `name`. |
| `/api/tcp/routers` | Lists all the TCP routers information. |
| `/api/tcp/routers/{name}` | Returns the information of the TCP router specified by `name`. |
| `/api/tcp/services` | Lists all the TCP services information. |
| `/api/tcp/services/{name}` | Returns the information of the TCP service specified by `name`. |
| `/api/version` | Returns information about Traefik version. |
| `/debug/vars` | See the [expvar](https://golang.org/pkg/expvar/) Go documentation. |
| `/debug/pprof/` | See the [pprof Index](https://golang.org/pkg/net/http/pprof/#Index) Go documentation. |
| `/debug/pprof/cmdline` | See the [pprof Cmdline](https://golang.org/pkg/net/http/pprof/#Cmdline) Go documentation. |
| `/debug/pprof/profile` | See the [pprof Profile](https://golang.org/pkg/net/http/pprof/#Profile) Go documentation. |
| `/debug/pprof/symbol` | See the [pprof Symbol](https://golang.org/pkg/net/http/pprof/#Symbol) Go documentation. |
| `/debug/pprof/trace` | See the [pprof Trace](https://golang.org/pkg/net/http/pprof/#Trace) Go documentation. |
## Common Configuration Use Cases
### Address / Port
You can define a custom address/port like this:
```toml
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.foo]
address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
[api]
entryPoint = "bar"
```
In the above example, you would access a service at /foo, an api endpoint, or the health-check as follows:
* Service: `http://hostname:80/foo`
* API: `http://hostname:8083/api/http/routers`
* Ping URL: `http://hostname:8082/ping`
### Authentication
To restrict access to the API handler, one can add authentication with the [basic auth middleware](../middlewares/basicauth.md).
```toml
[api]
middlewares=["api-auth"]
```
```toml
[http.middlewares]
[http.middlewares.api-auth.basicauth]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
```

View File

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

View File

@@ -1,15 +0,0 @@
# The Debug Mode
Getting More Information (Not For Production)
{: .subtitle }
The debug mode will make Traefik be _extremely_ verbose in its logs, and is NOT intended for production purposes.
## Configuration Example
??? example "TOML -- Enabling the Debug Mode"
```toml
[Global]
debug = true
```

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,74 +26,46 @@ Even if each provider is different, we can categorize them in four groups:
Below is the list of the currently supported providers in Traefik.
| Provider | Type | Configuration Type |
|---------------------------------|--------------|--------------------|
| [Docker](./docker.md) | Orchestrator | Label |
| [File](./file.md) | Orchestrator | Custom Annotation |
| [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource |
| [Marathon](marathon.md) | Orchestrator | Label |
| Provider | Type | Configuration Type |
|-----------------------------------|--------------|--------------------|
| [Docker](./docker.md) | Orchestrator | Label |
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource |
| [Marathon](./marathon.md) | Orchestrator | Label |
| [Rancher](./rancher.md) | Orchestrator | Label |
| [File](./file.md) | Manual | TOML format |
!!! note "More Providers"
The current version of Traefik is in development and doesn't support (yet) every provider. See the previous version (1.7) for more providers.
The current version of Traefik is in development and doesn't support (yet) every provider.
See the previous version (1.7) for more providers.
<!--
TODO (document TCP VS HTTP dynamic configuration)
-->
## Constraints Configuration
TODO (document TCP VS HTTP dynamic configuration)
-->
If you want to limit the scope of Traefik's service discovery, you can set constraints.
Doing so, Traefik will create routes for containers that match these constraints only.
## Restrict the Scope of Service Discovery
??? example "Containers with the api Tag"
By default Traefik will create routes for all detected containers.
```toml
constraints = ["tag==api"]
```
If you want to limit the scope of Traefik's service discovery,
i.e. disallow route creation for some containers,
you can do so in two different ways:
either with the generic configuration option `exposedByDefault`,
or with a finer granularity mechanism based on constraints.
??? example "Containers without the api Tag"
### `exposedByDefault` and `traefik.enable`
```toml
constraints = ["tag!=api"]
```
??? example "Containers with tags starting with 'us-'"
List of providers that support that feature:
```toml
constraints = ["tag==us-*"]
```
- [Docker](./docker.md#exposedbydefault)
- [Rancher](./rancher.md#exposedbydefault)
- [Marathon](./marathon.md#exposedbydefault)
??? example "Multiple constraints"
### Constraints
```toml
# Multiple constraints
# - "tag==" must match with at least one tag
# - "tag!=" must match with none of tags
constraints = ["tag!=us-*", "tag!=asia-*"]
```
List of providers that support constraints:
??? note "List of Providers that Support Constraints"
- Docker
- Consul K/V
- BoltDB
- Zookeeper
- ECS
- Etcd
- Consul Catalog
- Rancher
- Marathon
- Kubernetes (using a provider-specific mechanism based on label selectors)
!!! note
The constraint option belongs to the provider configuration itself.
??? example "Setting the Constraint Options for Docker"
```toml
[providers]
[providers.docker]
constraints = ["tag==api"]
```
- [Docker](./docker.md#constraints)
- [Rancher](./rancher.md#constraints)
- [Marathon](./marathon.md#constraints)
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)

View File

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

View File

@@ -0,0 +1,20 @@
# Enable Rancher Provider.
[Providers.Rancher]
# Expose Rancher services by default in Traefik.
ExposedByDefault = true
# Enable watch Rancher changes.
Watch = true
# Filter services with unhealthy states and inactive states.
EnableServiceHealthFilter = true
# Defines the polling interval (in seconds).
RefreshSeconds = true
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`, which is less accurate
IntervalPoll = false
# Prefix used for accessing the Rancher metadata service
Prefix = "/latest"

View File

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

View File

@@ -95,8 +95,11 @@
--api.dashboard (Default: "true")
Activate dashboard.
--api.debug (Default: "false")
Enable additional endpoints for debugging and profiling.
--api.entrypoint (Default: "traefik")
EntryPoint.
The entry point that the API handler will be bound to.
--api.middlewares (Default: "")
Middleware list.
@@ -153,9 +156,6 @@
--global.checknewversion (Default: "true")
Periodically check if a new version has been released.
--global.debug (Default: "false")
Enable debug mode.
--global.sendanonymoususage
Periodically send anonymous usage statistics. If the option is not specified, it
will be enabled by default.
@@ -172,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.
@@ -248,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.
@@ -381,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.
@@ -408,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.
@@ -456,17 +438,8 @@
Enable Rancher backend with default settings.
--providers.rancher.constraints (Default: "")
Filter services by constraint, matching with Traefik tags.
--providers.rancher.constraints[n].key (Default: "")
The provider label that will be matched against. In practice, it is always
'tag'.
--providers.rancher.constraints[n].mustmatch (Default: "false")
Whether the matching operator is equals or not equals.
--providers.rancher.constraints[n].value (Default: "")
The value that will be matched against.
Constraints is an expression that Traefik matches against the container's labels
to determine whether to create any route for that container.
--providers.rancher.defaultrule (Default: "Host(`{{ normalize .Name }}`)")
Default rule.

View File

@@ -93,8 +93,11 @@ Enable api/dashboard. (Default: ```false```)
`TRAEFIK_API_DASHBOARD`:
Activate dashboard. (Default: ```true```)
`TRAEFIK_API_DEBUG`:
Enable additional endpoints for debugging and profiling. (Default: ```false```)
`TRAEFIK_API_ENTRYPOINT`:
EntryPoint. (Default: ```traefik```)
The entry point that the API handler will be bound to. (Default: ```traefik```)
`TRAEFIK_API_MIDDLEWARES`:
Middleware list.
@@ -147,9 +150,6 @@ WriteTimeout is the maximum duration before timing out writes of the response. I
`TRAEFIK_GLOBAL_CHECKNEWVERSION`:
Periodically check if a new version has been released. (Default: ```false```)
`TRAEFIK_GLOBAL_DEBUG`:
Enable debug mode. (Default: ```false```)
`TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE`:
Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default.
@@ -165,6 +165,9 @@ resolv.conf used for DNS resolving (Default: ```/etc/resolv.conf```)
`TRAEFIK_HOSTRESOLVER_RESOLVDEPTH`:
The maximal depth of DNS recursive resolving (Default: ```5```)
`TRAEFIK_LOG`:
Traefik log settings. (Default: "false")
`TRAEFIK_LOG_FILEPATH`:
Traefik log file path. Stdout is used when omitted or empty.
@@ -241,16 +244,7 @@ Middleware list.
Enable Docker backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags.
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_DOCKER_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
`TRAEFIK_PROVIDERS_DOCKER_DEFAULTRULE`:
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
@@ -373,16 +367,7 @@ Basic authentication User.
Basic authentication Password.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_MARATHON_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application.
`TRAEFIK_PROVIDERS_MARATHON_DCOSTOKEN`:
DCOSToken for DCOS environment, This will override the Authorization header.
@@ -399,9 +384,6 @@ Marathon server endpoint. You can also specify multiple endpoint for Marathon. (
`TRAEFIK_PROVIDERS_MARATHON_EXPOSEDBYDEFAULT`:
Expose Marathon apps by default. (Default: ```true```)
`TRAEFIK_PROVIDERS_MARATHON_FILTERMARATHONCONSTRAINTS`:
Enable use of Marathon constraints in constraint filtering. (Default: ```false```)
`TRAEFIK_PROVIDERS_MARATHON_FORCETASKHOSTNAME`:
Force to use the task's hostname. (Default: ```false```)
@@ -445,16 +427,7 @@ Backends throttle duration: minimum duration between 2 events from providers bef
Enable Rancher backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS`:
Filter services by constraint, matching with Traefik tags.
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_KEY`:
The provider label that will be matched against. In practice, it is always 'tag'.
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_MUSTMATCH`:
Whether the matching operator is equals or not equals. (Default: ```false```)
`TRAEFIK_PROVIDERS_RANCHER_CONSTRAINTS[n]_VALUE`:
The value that will be matched against.
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
`TRAEFIK_PROVIDERS_RANCHER_DEFAULTRULE`:
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)

View File

@@ -1,5 +1,4 @@
[Global]
Debug = true
CheckNewVersion = true
SendAnonymousUsage = true
@@ -42,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"
@@ -74,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"
@@ -135,16 +115,7 @@
RefreshSeconds = 42
IntervalPoll = true
Prefix = "foobar"
[[Providers.Rancher.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
[[Providers.Rancher.Constraints]]
Key = "foobar"
MustMatch = true
Regex = "foobar"
Constraints = "foobar"
[API]
EntryPoint = "foobar"

View File

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

View File

@@ -36,8 +36,10 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
[entryPoints.mysql-default]
address = ":80"
[entryPoints.mysql-default]
address = ":3306"
address = ":3306"
```
```toml
[tcp]
[tcp.routers]
[tcp.routers.to-database]
@@ -50,8 +52,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
### EntryPoints
If not specified, HTTP routers will accept requests from all defined entrypoints.
If you want to limit the router scope to a set of entrypoints, set the entrypoints option.
If not specified, HTTP routers will accept requests from all defined entry points.
If you want to limit the router scope to a set of entry points, set the `entryPoints` option.
??? example "Listens to Every EntryPoint"
@@ -63,7 +65,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
# ...
[entryPoints.other]
# ...
```
```toml
[http.routers]
[http.routers.Router-1]
# By default, routers listen to every entrypoints
@@ -81,7 +85,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
# ...
[entryPoints.other]
# ...
```
```toml
[http.routers]
[http.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
@@ -97,26 +103,27 @@ 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 |
|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| ``Headers(`key`, `value`)`` | Check if there is a key `key`defined in the headers, with the value `value` |
| ``HeadersRegexp(`key`, `regexp`)`` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` |
| ``Host(`domain-1`, ...)`` | Check if the request domain targets one of the given `domains`. |
| ``HostRegexp(`traefik.io`, `{subdomain:[a-z]+}.traefik.io`, ...)`` | Check if the request domain matches the given `regexp`. |
| `Method(methods, ...)` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) |
| ``Path(`path`, `/articles/{category}/{id:[0-9]+}`, ...)`` | Match exact request path. It accepts a sequence of literal and regular expression paths. |
| ``PathPrefix(`/products/`, `/articles/{category}/{id:[0-9]+}`)`` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. |
| ``Query(`foo=bar`, `bar=baz`)`` | Match` Query String parameters. It accepts a sequence of key=value pairs. |
| Rule | Description |
|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` |
| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` |
| ```Host(`domain-1`, ...)``` | Check if the request domain targets one of the given `domains`. |
| ```HostRegexp(`traefik.io`, `{subdomain:[a-z]+}.traefik.io`, ...)``` | Check if the request domain matches the given `regexp`. |
| `Method(methods, ...)` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) |
| ```Path(`path`, `/articles/{category}/{id:[0-9]+}`, ...)``` | Match exact request path. It accepts a sequence of literal and regular expression paths. |
| ```PathPrefix(`/products/`, `/articles/{category}/{id:[0-9]+}`)``` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. |
| ```Query(`foo=bar`, `bar=baz`)``` | Match` Query String parameters. It accepts a sequence of key=value pairs. |
!!! important "Regexp Syntax"
@@ -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"
@@ -173,7 +180,7 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
!!! note "HTTPS & ACME"
In the current version, with [ACME](../../https-tls/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
!!! note "Passthrough"
@@ -200,7 +207,7 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
#### `Options`
The `Options` field enables fine-grained control of the TLS parameters.
It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and will be applied only if a `Host` rule is defined.
It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied only if a `Host` rule is defined.
??? example "Configuring the tls options"
@@ -212,7 +219,6 @@ It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and
[http.routers.Router-1.tls] # will terminate the TLS request
options = "foo"
[tlsOptions]
[tlsOptions.foo]
minVersion = "VersionTLS12"
@@ -226,15 +232,15 @@ It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and
### General
If both HTTP routers and TCP routers listen to the same entrypoints, the TCP routers will apply *before* the HTTP routers.
If both HTTP routers and TCP routers listen to the same entry points, the TCP routers will apply *before* the HTTP routers.
If no matching route is found for the TCP routers, then the HTTP routers will take over.
### EntryPoints
If not specified, TCP 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, 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 entrypoints, set the entrypoin
# ...
[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 entrypoints, set the entrypoin
[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 entrypoints, set the entrypoin
# ...
[entryPoints.other]
# ...
```
```toml
[tcp.routers]
[tcp.routers.Router-1]
entryPoints = ["web-secure", "other"] # won't listen to entrypoint web
@@ -274,9 +284,9 @@ If you want to limit the router scope to a set of entrypoints, set the entrypoin
### Rule
| Rule | Description |
|------------------------------|-------------------------------------------------------------------------|
| ``HostSNI(`domain-1`, ...)`` | Check if the Server Name Indication corresponds to the given `domains`. |
| Rule | Description |
|--------------------------------|-------------------------------------------------------------------------|
| ```HostSNI(`domain-1`, ...)``` | Check if the Server Name Indication corresponds to the given `domains`. |
!!! important "HostSNI & TLS"
@@ -305,7 +315,7 @@ Services are the target for the router.
```toml
[tcp.routers]
[tcp.routers.Router-1]
rule = "Host(`foo-domain`)"
rule = "HostSNI(`foo-domain`)"
service = "service-id"
[tcp.routers.Router-1.tls] # will terminate the TLS request by default
```
@@ -315,7 +325,7 @@ Services are the target for the router.
```toml
[tcp.routers]
[tcp.routers.Router-1]
rule = "Host(`foo-domain`)"
rule = "HostSNI(`foo-domain`)"
service = "service-id"
[tcp.routers.Router-1.tls]
passthrough=true
@@ -323,24 +333,23 @@ Services are the target for the router.
!!! note "TLS & ACME"
In the current version, with [ACME](../../https-tls/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
#### `Options`
The `Options` field enables fine-grained control of the TLS parameters.
It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and will be applied only if a `HostSNI` rule is defined.
It refers to a [tlsOptions](../../https/tls.md#tls-options) and will be applied only if a `HostSNI` rule is defined.
??? example "Configuring the tls options"
```toml
[tcp.routers]
[tcp.routers.Router-1]
rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
rule = "HostSNI(`foo-domain`)"
service = "service-id"
[tcp.routers.Router-1.tls] # will terminate the TLS request
options = "foo"
[tlsOptions]
[tlsOptions.foo]
minVersion = "VersionTLS12"

View File

@@ -1,5 +1,5 @@
server:
image: rancher/k3s:v0.2.0
image: rancher/k3s:v0.5.0
command: server --disable-agent --no-deploy traefik
environment:
- K3S_CLUSTER_SECRET=somethingtotallyrandom
@@ -17,7 +17,7 @@ server:
- 6443:6443
node:
image: rancher/k3s:v0.2.0
image: rancher/k3s:v0.5.0
privileged: true
links:
- server

View File

@@ -85,8 +85,9 @@ nav:
- 'Routers': 'routing/routers/index.md'
- 'Services': 'routing/services/index.md'
- 'HTTPS & TLS':
- 'Overview': 'https-tls/overview.md'
- 'ACME': 'https-tls/acme.md'
- 'Overview': 'https/overview.md'
- 'TLS': 'https/tls.md'
- 'Let''s Encrypt': 'https/acme.md'
- 'Middlewares':
- 'Overview': 'middlewares/overview.md'
- 'AddPrefix': 'middlewares/addprefix.md'
@@ -113,8 +114,8 @@ nav:
- 'Operations':
- 'CLI': 'operations/cli.md'
- 'Dashboard' : 'operations/dashboard.md'
- 'API': 'operations/api.md'
- 'Ping': 'operations/ping.md'
- 'Debug Mode': 'operations/debug-mode.md'
- 'Observability':
- 'Logs': 'observability/logs.md'
- 'Access Logs': 'observability/access-logs.md'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
server:
image: rancher/k3s:v0.2.0
image: rancher/k3s:v0.5.0
command: server --disable-agent --no-deploy traefik
environment:
- K3S_CLUSTER_SECRET=somethingtotallyrandom
@@ -12,7 +12,7 @@ server:
- 6443:6443
node:
image: rancher/k3s:v0.2.0
image: rancher/k3s:v0.5.0
privileged: true
links:
- server

View File

@@ -161,7 +161,7 @@ func (s *SimpleSuite) TestApiOnSameEntryPoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api.entryPoint=http", "--global.debug", "--providers.docker")
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api.entryPoint=http", "--log.level=DEBUG", "--providers.docker")
defer output(c)
err := cmd.Start()
@@ -241,7 +241,7 @@ func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--global.debug", "--providers.docker", "--api")
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--log.level=DEBUG", "--providers.docker", "--api")
defer output(c)
err := cmd.Start()
@@ -259,7 +259,7 @@ func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--global.debug", "--providers.docker", "--api")
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--log.level=DEBUG", "--providers.docker", "--api")
defer output(c)
err := cmd.Start()
@@ -277,7 +277,7 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api", "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0", "--providers.docker", "--global.debug")
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api", "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0", "--providers.docker", "--log.level=DEBUG")
defer output(c)
err := cmd.Start()
@@ -444,8 +444,8 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
Routers: map[string]*config.Router{
"router1": {
EntryPoints: []string{"web"},
Middlewares: []string{"file.customheader"},
Service: "file.service",
Middlewares: []string{"customheader@file"},
Service: "service@file",
Rule: "PathPrefix(`/`)",
},
},

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,7 @@ func (s *WebsocketSuite) TestBase(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -99,7 +99,7 @@ func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -149,7 +149,7 @@ func (s *WebsocketSuite) TestOrigin(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -210,7 +210,7 @@ func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -268,7 +268,7 @@ func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -331,7 +331,7 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -375,7 +375,7 @@ func (s *WebsocketSuite) TestSpecificResponseFromBackend(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -421,7 +421,7 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
@@ -476,7 +476,7 @@ func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug", "--accesslog")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
defer display(c)
err := cmd.Start()
@@ -535,7 +535,7 @@ func (s *WebsocketSuite) TestHeaderAreForwared(c *check.C) {
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--global.debug")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()

View File

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

View File

@@ -1,8 +1,12 @@
package api
import (
"io"
"encoding/json"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"github.com/containous/mux"
"github.com/containous/traefik/pkg/config"
@@ -11,14 +15,14 @@ import (
"github.com/containous/traefik/pkg/types"
"github.com/containous/traefik/pkg/version"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/unrolled/render"
)
var templateRenderer jsonRenderer = render.New(render.Options{Directory: "nowhere"})
const (
defaultPerPage = 100
defaultPage = 1
)
type jsonRenderer interface {
JSON(w io.Writer, status int, v interface{}) error
}
const nextPageHeader = "X-Next-Page"
type serviceInfoRepresentation struct {
*config.ServiceInfo
@@ -34,6 +38,43 @@ type RunTimeRepresentation struct {
TCPServices map[string]*config.TCPServiceInfo `json:"tcpServices,omitempty"`
}
type routerRepresentation struct {
*config.RouterInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
type serviceRepresentation struct {
*config.ServiceInfo
ServerStatus map[string]string `json:"serverStatus,omitempty"`
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
type middlewareRepresentation struct {
*config.MiddlewareInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
type tcpRouterRepresentation struct {
*config.TCPRouterInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
type tcpServiceRepresentation struct {
*config.TCPServiceInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
type pageInfo struct {
startIndex int
endIndex int
nextPage int
}
// Handler serves the configuration and status of Traefik on API endpoints.
type Handler struct {
dashboard bool
@@ -59,7 +100,7 @@ func New(staticConfig static.Configuration, runtimeConfig *config.RuntimeConfigu
statistics: staticConfig.API.Statistics,
dashboardAssets: staticConfig.API.DashboardAssets,
runtimeConfiguration: rConfig,
debug: staticConfig.Global.Debug,
debug: staticConfig.API.Debug,
}
}
@@ -71,9 +112,21 @@ func (h Handler) Append(router *mux.Router) {
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRuntimeConfiguration)
router.Methods(http.MethodGet).Path("/api/http/routers").HandlerFunc(h.getRouters)
router.Methods(http.MethodGet).Path("/api/http/routers/{routerID}").HandlerFunc(h.getRouter)
router.Methods(http.MethodGet).Path("/api/http/services").HandlerFunc(h.getServices)
router.Methods(http.MethodGet).Path("/api/http/services/{serviceID}").HandlerFunc(h.getService)
router.Methods(http.MethodGet).Path("/api/http/middlewares").HandlerFunc(h.getMiddlewares)
router.Methods(http.MethodGet).Path("/api/http/middlewares/{middlewareID}").HandlerFunc(h.getMiddleware)
router.Methods(http.MethodGet).Path("/api/tcp/routers").HandlerFunc(h.getTCPRouters)
router.Methods(http.MethodGet).Path("/api/tcp/routers/{routerID}").HandlerFunc(h.getTCPRouter)
router.Methods(http.MethodGet).Path("/api/tcp/services").HandlerFunc(h.getTCPServices)
router.Methods(http.MethodGet).Path("/api/tcp/services/{serviceID}").HandlerFunc(h.getTCPService)
// FIXME stats
// health route
//router.Methods(http.MethodGet).Path("/health").HandlerFunc(p.getHealthHandler)
// router.Methods(http.MethodGet).Path("/health").HandlerFunc(p.getHealthHandler)
version.Handler{}.Append(router)
@@ -82,6 +135,268 @@ func (h Handler) Append(router *mux.Router) {
}
}
func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
results := make([]routerRepresentation, 0, len(h.runtimeConfiguration.Routers))
for name, rt := range h.runtimeConfiguration.Routers {
results = append(results, routerRepresentation{
RouterInfo: rt,
Name: name,
Provider: getProviderName(name),
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
pageInfo, err := pagination(request, len(results))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
router, ok := h.runtimeConfiguration.Routers[routerID]
if !ok {
http.NotFound(rw, request)
return
}
result := routerRepresentation{
RouterInfo: router,
Name: routerID,
Provider: getProviderName(routerID),
}
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
results := make([]serviceRepresentation, 0, len(h.runtimeConfiguration.Services))
for name, si := range h.runtimeConfiguration.Services {
results = append(results, serviceRepresentation{
ServiceInfo: si,
Name: name,
Provider: getProviderName(name),
ServerStatus: si.GetAllStatus(),
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
pageInfo, err := pagination(request, len(results))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
service, ok := h.runtimeConfiguration.Services[serviceID]
if !ok {
http.NotFound(rw, request)
return
}
result := serviceRepresentation{
ServiceInfo: service,
Name: serviceID,
Provider: getProviderName(serviceID),
ServerStatus: service.GetAllStatus(),
}
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
results := make([]middlewareRepresentation, 0, len(h.runtimeConfiguration.Middlewares))
for name, mi := range h.runtimeConfiguration.Middlewares {
results = append(results, middlewareRepresentation{
MiddlewareInfo: mi,
Name: name,
Provider: getProviderName(name),
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
pageInfo, err := pagination(request, len(results))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
middleware, ok := h.runtimeConfiguration.Middlewares[middlewareID]
if !ok {
http.NotFound(rw, request)
return
}
result := middlewareRepresentation{
MiddlewareInfo: middleware,
Name: middlewareID,
Provider: getProviderName(middlewareID),
}
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
results := make([]tcpRouterRepresentation, 0, len(h.runtimeConfiguration.TCPRouters))
for name, rt := range h.runtimeConfiguration.TCPRouters {
results = append(results, tcpRouterRepresentation{
TCPRouterInfo: rt,
Name: name,
Provider: getProviderName(name),
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
pageInfo, err := pagination(request, len(results))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
router, ok := h.runtimeConfiguration.TCPRouters[routerID]
if !ok {
http.NotFound(rw, request)
return
}
result := tcpRouterRepresentation{
TCPRouterInfo: router,
Name: routerID,
Provider: getProviderName(routerID),
}
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
results := make([]tcpServiceRepresentation, 0, len(h.runtimeConfiguration.TCPServices))
for name, si := range h.runtimeConfiguration.TCPServices {
results = append(results, tcpServiceRepresentation{
TCPServiceInfo: si,
Name: name,
Provider: getProviderName(name),
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
pageInfo, err := pagination(request, len(results))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
service, ok := h.runtimeConfiguration.TCPServices[serviceID]
if !ok {
http.NotFound(rw, request)
return
}
result := tcpServiceRepresentation{
TCPServiceInfo: service,
Name: serviceID,
Provider: getProviderName(serviceID),
}
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.Request) {
siRepr := make(map[string]*serviceInfoRepresentation, len(h.runtimeConfiguration.Services))
for k, v := range h.runtimeConfiguration.Services {
@@ -91,7 +406,7 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
}
}
rtRepr := RunTimeRepresentation{
result := RunTimeRepresentation{
Routers: h.runtimeConfiguration.Routers,
Middlewares: h.runtimeConfiguration.Middlewares,
Services: siRepr,
@@ -99,9 +414,55 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
TCPServices: h.runtimeConfiguration.TCPServices,
}
err := templateRenderer.JSON(rw, http.StatusOK, rtRepr)
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
}
func pagination(request *http.Request, max int) (pageInfo, error) {
perPage, err := getIntParam(request, "per_page", defaultPerPage)
if err != nil {
return pageInfo{}, err
}
page, err := getIntParam(request, "page", defaultPage)
if err != nil {
return pageInfo{}, err
}
startIndex := (page - 1) * perPage
if startIndex != 0 && startIndex >= max {
return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage)
}
endIndex := startIndex + perPage
if endIndex >= max {
endIndex = max
}
nextPage := 1
if page*perPage < max {
nextPage = page + 1
}
return pageInfo{startIndex: startIndex, endIndex: endIndex, nextPage: nextPage}, nil
}
func getIntParam(request *http.Request, key string, defaultValue int) (int, error) {
raw := request.URL.Query().Get(key)
if raw == "" {
return defaultValue, nil
}
value, err := strconv.Atoi(raw)
if err != nil || value <= 0 {
return 0, fmt.Errorf("invalid request: %s: %d", key, value)
}
return value, nil
}
func getProviderName(id string) string {
return strings.SplitN(id, "@", 2)[1]
}

File diff suppressed because it is too large Load Diff

View File

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

13
pkg/api/testdata/middleware-auth.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"basicAuth": {
"users": [
"admin:admin"
]
},
"name": "auth@myprovider",
"provider": "myprovider",
"usedBy": [
"bar@myprovider",
"test@myprovider"
]
}

View File

@@ -0,0 +1 @@
[]

12
pkg/api/testdata/middlewares-page2.json vendored Normal file
View File

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

35
pkg/api/testdata/middlewares.json vendored Normal file
View File

@@ -0,0 +1,35 @@
[
{
"addPrefix": {
"prefix": "/toto"
},
"name": "addPrefixTest@anotherprovider",
"provider": "anotherprovider",
"usedBy": [
"bar@myprovider"
]
},
{
"addPrefix": {
"prefix": "/titi"
},
"name": "addPrefixTest@myprovider",
"provider": "myprovider",
"usedBy": [
"test@myprovider"
]
},
{
"basicAuth": {
"users": [
"admin:admin"
]
},
"name": "auth@myprovider",
"provider": "myprovider",
"usedBy": [
"bar@myprovider",
"test@myprovider"
]
}
]

13
pkg/api/testdata/router-bar.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"entryPoints": [
"web"
],
"middlewares": [
"auth",
"addPrefixTest@anotherprovider"
],
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider"
}

1
pkg/api/testdata/routers-empty.json vendored Normal file
View File

@@ -0,0 +1 @@
[]

View File

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

11
pkg/api/testdata/routers-page2.json vendored Normal file
View File

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

28
pkg/api/testdata/routers.json vendored Normal file
View File

@@ -0,0 +1,28 @@
[
{
"entryPoints": [
"web"
],
"middlewares": [
"auth",
"addPrefixTest@anotherprovider"
],
"name": "bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider"
},
{
"entryPoints": [
"web"
],
"middlewares": [
"addPrefixTest",
"auth"
],
"name": "test@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar.other`)",
"service": "foo-service@myprovider"
}
]

19
pkg/api/testdata/service-bar.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"loadbalancer": {
"passHostHeader": false,
"servers": [
{
"url": "http://127.0.0.1"
}
]
},
"name": "bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

1
pkg/api/testdata/services-empty.json vendored Normal file
View File

@@ -0,0 +1 @@
[]

20
pkg/api/testdata/services-page2.json vendored Normal file
View File

@@ -0,0 +1,20 @@
[
{
"loadbalancer": {
"passHostHeader": false,
"servers": [
{
"url": "http://127.0.0.2"
}
]
},
"name": "baz@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.2": "UP"
},
"usedBy": [
"foo@myprovider"
]
}
]

39
pkg/api/testdata/services.json vendored Normal file
View File

@@ -0,0 +1,39 @@
[
{
"loadbalancer": {
"passHostHeader": false,
"servers": [
{
"url": "http://127.0.0.1"
}
]
},
"name": "bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
},
{
"loadbalancer": {
"passHostHeader": false,
"servers": [
{
"url": "http://127.0.0.2"
}
]
},
"name": "baz@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.2": "UP"
},
"usedBy": [
"foo@myprovider"
]
}
]

9
pkg/api/testdata/tcprouter-bar.json vendored Normal file
View File

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

View File

@@ -0,0 +1 @@
[]

11
pkg/api/testdata/tcprouters-page2.json vendored Normal file
View File

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

23
pkg/api/testdata/tcprouters.json vendored Normal file
View File

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

15
pkg/api/testdata/tcpservice-bar.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"loadbalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "bar@myprovider",
"provider": "myprovider",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View File

@@ -0,0 +1 @@
[]

16
pkg/api/testdata/tcpservices-page2.json vendored Normal file
View File

@@ -0,0 +1,16 @@
[
{
"loadbalancer": {
"servers": [
{
"address": "127.0.0.2:2345"
}
]
},
"name": "baz@myprovider",
"provider": "myprovider",
"usedBy": [
"foo@myprovider"
]
}
]

31
pkg/api/testdata/tcpservices.json vendored Normal file
View File

@@ -0,0 +1,31 @@
[
{
"loadbalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "bar@myprovider",
"provider": "myprovider",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
},
{
"loadbalancer": {
"servers": [
{
"address": "127.0.0.2:2345"
}
]
},
"name": "baz@myprovider",
"provider": "myprovider",
"usedBy": [
"foo@myprovider"
]
}
]

View File

@@ -3,7 +3,6 @@
package cli
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -17,7 +16,9 @@ type Command struct {
Resources []ResourceLoader
Run func([]string) error
Hidden bool
subCommands []*Command
// AllowArg if not set, disallows any argument that is not a known command or a sub-command.
AllowArg bool
subCommands []*Command
}
// AddCommand Adds a sub command.
@@ -40,13 +41,17 @@ func Execute(cmd *Command) error {
}
func execute(cmd *Command, args []string, root bool) error {
// Calls command without args.
if len(args) == 1 {
if err := run(cmd, args); err != nil {
if err := run(cmd, args[1:]); err != nil {
return fmt.Errorf("command %s error: %v", args[0], err)
}
return nil
}
// Special case: if the command is the top level one,
// and the first arg (`args[1]`) is not the command name or a known sub-command,
// then we run the top level command itself.
if root && cmd.Name != args[1] && !contains(cmd.subCommands, args[1]) {
if err := run(cmd, args[1:]); err != nil {
return fmt.Errorf("command %s error: %v", filepath.Base(args[0]), err)
@@ -54,6 +59,7 @@ func execute(cmd *Command, args []string, root bool) error {
return nil
}
// Calls command by its name.
if len(args) >= 2 && cmd.Name == args[1] {
if err := run(cmd, args[2:]); err != nil {
return fmt.Errorf("command %s error: %v", cmd.Name, err)
@@ -61,6 +67,7 @@ func execute(cmd *Command, args []string, root bool) error {
return nil
}
// No sub-command, calls the current command.
if len(cmd.subCommands) == 0 {
if err := run(cmd, args[1:]); err != nil {
return fmt.Errorf("command %s error: %v", cmd.Name, err)
@@ -68,6 +75,7 @@ func execute(cmd *Command, args []string, root bool) error {
return nil
}
// Trying to find the sub-command.
for _, subCmd := range cmd.subCommands {
if len(args) >= 2 && subCmd.Name == args[1] {
return execute(subCmd, args[1:], false)
@@ -78,13 +86,18 @@ func execute(cmd *Command, args []string, root bool) error {
}
func run(cmd *Command, args []string) error {
if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
_ = PrintHelp(os.Stdout, cmd)
return fmt.Errorf("command not found: %s", args[0])
}
if isHelp(args) {
return PrintHelp(os.Stdout, cmd)
}
if cmd.Run == nil {
_ = PrintHelp(os.Stdout, cmd)
return errors.New("command not found")
return fmt.Errorf("command %s is not runnable", cmd.Name)
}
if cmd.Configuration == nil {
@@ -113,3 +126,7 @@ func contains(cmds []*Command, name string) bool {
return false
}
func isFlag(arg string) bool {
return len(arg) > 0 && arg[0] == '-'
}

View File

@@ -86,6 +86,39 @@ func Test_execute(t *testing.T) {
},
expected: expected{result: "root"},
},
{
desc: "root command, with argument, command not found",
args: []string{"", "echo"},
command: func() *Command {
return &Command{
Name: "root",
Description: "This is a test",
Configuration: nil,
Run: func(_ []string) error {
called = "root"
return nil
},
}
},
expected: expected{error: true},
},
{
desc: "root command, call help, with argument, command not found",
args: []string{"", "echo", "--help"},
command: func() *Command {
return &Command{
Name: "root",
Description: "This is a test",
Configuration: nil,
Run: func(_ []string) error {
called = "root"
return nil
},
}
},
expected: expected{error: true},
},
{
desc: "one sub command",
args: []string{"", "sub1"},
@@ -114,6 +147,34 @@ func Test_execute(t *testing.T) {
},
expected: expected{result: "sub1"},
},
{
desc: "one sub command, with argument, command not found",
args: []string{"", "sub1", "echo"},
command: func() *Command {
rootCmd := &Command{
Name: "test",
Description: "This is a test",
Configuration: nil,
Run: func(_ []string) error {
called += "root"
return nil
},
}
_ = rootCmd.AddCommand(&Command{
Name: "sub1",
Description: "sub1",
Configuration: nil,
Run: func(_ []string) error {
called += "sub1"
return nil
},
})
return rootCmd
},
expected: expected{error: true},
},
{
desc: "two sub commands",
args: []string{"", "sub2"},
@@ -376,6 +437,7 @@ func Test_execute(t *testing.T) {
Name: "sub1",
Description: "sub1",
Configuration: nil,
AllowArg: true,
Run: func(args []string) error {
called += "sub1-" + strings.Join(args, "-")
return nil
@@ -394,6 +456,7 @@ func Test_execute(t *testing.T) {
Name: "root",
Description: "This is a test",
Configuration: nil,
AllowArg: true,
Run: func(args []string) error {
called += "root-" + strings.Join(args, "-")
return nil

View File

@@ -3,7 +3,6 @@ package cli
import (
"fmt"
"os"
"strings"
"github.com/containous/traefik/pkg/config/env"
"github.com/containous/traefik/pkg/log"
@@ -14,23 +13,12 @@ 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 {
if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err)
}

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

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

View File

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,8 +19,8 @@ type Middleware struct {
Headers *Headers `json:"headers,omitempty"`
Errors *ErrorPage `json:"errors,omitempty"`
RateLimit *RateLimit `json:"rateLimit,omitempty"`
RedirectRegex *RedirectRegex `json:"redirectregex,omitempty"`
RedirectScheme *RedirectScheme `json:"redirectscheme,omitempty"`
RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty"`
RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty"`
BasicAuth *BasicAuth `json:"basicAuth,omitempty"`
DigestAuth *DigestAuth `json:"digestAuth,omitempty"`
ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty"`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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