Merge branch 'v2.1' into master
This commit is contained in:
commit
829649e905
73 changed files with 1497 additions and 517 deletions
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -1,3 +1,42 @@
|
||||||
|
## [v2.1.0-rc3](https://github.com/containous/traefik/tree/v2.1.0-rc3) (2019-12-02)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v2.1.0-rc2...v2.1.0-rc3)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[cli]** fix: sub command help ([#5887](https://github.com/containous/traefik/pull/5887) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog]** fix: consul catalog constraints. ([#5913](https://github.com/containous/traefik/pull/5913) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog]** Service registered with same id on Consul Catalog ([#5900](https://github.com/containous/traefik/pull/5900) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[webui]** Web UI: Avoid polling on /api/entrypoints ([#5863](https://github.com/containous/traefik/pull/5863) by [matthieuh](https://github.com/matthieuh))
|
||||||
|
- **[webui]** Web UI: Sync toolbar table state with url query params ([#5861](https://github.com/containous/traefik/pull/5861) by [matthieuh](https://github.com/matthieuh))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- **[cli]** Add custom help function to command ([#5923](https://github.com/containous/traefik/pull/5923) by [Ullaakut](https://github.com/Ullaakut))
|
||||||
|
|
||||||
|
## [v2.0.6](https://github.com/containous/traefik/tree/v2.0.6) (2019-12-02)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.5...v2.0.6)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme]** Update go-acme/lego to 3.2.0 ([#5839](https://github.com/containous/traefik/pull/5839) by [kolaente](https://github.com/kolaente))
|
||||||
|
- **[cli,healthcheck]** Uses, if it exists, the ping entry point provided in the static configuration ([#5867](https://github.com/containous/traefik/pull/5867) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||||
|
- **[healthcheck]** Healthcheck managed for all related services ([#5860](https://github.com/containous/traefik/pull/5860) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||||
|
- **[logs,middleware]** Do not give responsewriter or its headers to asynchronous logging goroutine ([#5840](https://github.com/containous/traefik/pull/5840) by [mpl](https://github.com/mpl))
|
||||||
|
- **[middleware]** X-Forwarded-Proto must not skip the redirection. ([#5836](https://github.com/containous/traefik/pull/5836) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** fix: location header rewrite. ([#5835](https://github.com/containous/traefik/pull/5835) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Remove Request Headers CORS Preflight Requirement ([#5903](https://github.com/containous/traefik/pull/5903) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[rancher]** Change service name in rancher provider to make webui service details view work ([#5895](https://github.com/containous/traefik/pull/5895) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[tracing]** Fix extraction for zipkin tracing ([#5920](https://github.com/containous/traefik/pull/5920) by [jcchavezs](https://github.com/jcchavezs))
|
||||||
|
- **[webui]** Web UI: Avoid unnecessary duplicated api calls ([#5884](https://github.com/containous/traefik/pull/5884) by [matthieuh](https://github.com/matthieuh))
|
||||||
|
- **[webui]** Web UI: Avoid some router properties to overflow their container ([#5872](https://github.com/containous/traefik/pull/5872) by [matthieuh](https://github.com/matthieuh))
|
||||||
|
- **[webui]** Web UI: Fix displayed tcp service details ([#5868](https://github.com/containous/traefik/pull/5868) by [matthieuh](https://github.com/matthieuh))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** doc: fix wrong acme information ([#5837](https://github.com/containous/traefik/pull/5837) by [ldez](https://github.com/ldez))
|
||||||
|
- **[docker,docker/swarm]** Add Swarm section to the Docker Provider Documentation ([#5874](https://github.com/containous/traefik/pull/5874) by [dduportal](https://github.com/dduportal))
|
||||||
|
- **[docker]** Update router entrypoint example ([#5766](https://github.com/containous/traefik/pull/5766) by [woto](https://github.com/woto))
|
||||||
|
- **[k8s/helm]** Mention the experimental Helm Chart in the installation section of documentation ([#5879](https://github.com/containous/traefik/pull/5879) by [dduportal](https://github.com/dduportal))
|
||||||
|
- doc: remove double quotes on CLI flags. ([#5862](https://github.com/containous/traefik/pull/5862) by [ldez](https://github.com/ldez))
|
||||||
|
- Fixed spelling error ([#5834](https://github.com/containous/traefik/pull/5834) by [blakebuthod](https://github.com/blakebuthod))
|
||||||
|
- Add back the security section from v1 ([#5832](https://github.com/containous/traefik/pull/5832) by [pascalandy](https://github.com/pascalandy))
|
||||||
|
|
||||||
## [v2.1.0-rc2](https://github.com/containous/traefik/tree/v2.0.4) (2019-11-15)
|
## [v2.1.0-rc2](https://github.com/containous/traefik/tree/v2.0.4) (2019-11-15)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc1...v2.1.0-rc2)
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc1...v2.1.0-rc2)
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,14 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
||||||
return nil, errors.New("please enable `ping` to use health check")
|
return nil, errors.New("please enable `ping` to use health check")
|
||||||
}
|
}
|
||||||
|
|
||||||
pingEntryPoint, ok := staticConfiguration.EntryPoints["traefik"]
|
ep := staticConfiguration.Ping.EntryPoint
|
||||||
|
if ep == "" {
|
||||||
|
ep = "traefik"
|
||||||
|
}
|
||||||
|
|
||||||
|
pingEntryPoint, ok := staticConfiguration.EntryPoints[ep]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("missing `ping` entrypoint")
|
return nil, fmt.Errorf("ping: missing %s entry point", ep)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{Timeout: 5 * time.Second}
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
You can install Traefik with the following flavors:
|
You can install Traefik with the following flavors:
|
||||||
|
|
||||||
* [Use the official Docker image](./#use-the-official-docker-image)
|
* [Use the official Docker image](./#use-the-official-docker-image)
|
||||||
|
* [(Experimental) Use the Helm Chart](./#use-the-helm-chart)
|
||||||
* [Use the binary distribution](./#use-the-binary-distribution)
|
* [Use the binary distribution](./#use-the-binary-distribution)
|
||||||
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
||||||
|
|
||||||
|
@ -24,6 +25,70 @@ For more details, go to the [Docker provider documentation](../providers/docker.
|
||||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||||
* All the orchestrator using docker images could fetch the official Traefik docker image.
|
* All the orchestrator using docker images could fetch the official Traefik docker image.
|
||||||
|
|
||||||
|
## Use the Helm Chart
|
||||||
|
|
||||||
|
!!! warning "Experimental Helm Chart"
|
||||||
|
|
||||||
|
Please note that the Helm Chart for Traefik v2 is still experimental.
|
||||||
|
|
||||||
|
The Traefik Stable Chart from
|
||||||
|
[Helm's default charts repository](https://github.com/helm/charts/tree/master/stable/traefik) is still using [Traefik v1.7](https://docs.traefik.io/v1.7).
|
||||||
|
|
||||||
|
Traefik can be installed in Kubernetes using the v2.0 Helm chart from <https://github.com/containous/traefik-helm-chart>.
|
||||||
|
|
||||||
|
Ensure that the following requirements are met:
|
||||||
|
|
||||||
|
* Kubernetes 1.14+
|
||||||
|
* Helm version 2.x is [installed](https://v2.helm.sh/docs/using_helm/) and initialized with Tiller
|
||||||
|
|
||||||
|
Retrieve the latest chart version from the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Retrieve Chart from the repository
|
||||||
|
git clone https://github.com/containous/traefik-helm-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
And install it with the `helm` command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install ./traefik-helm-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "Helm Features"
|
||||||
|
|
||||||
|
All [Helm features](https://v2.helm.sh/docs/using_helm/#using-helm) are supported.
|
||||||
|
For instance, installing the chart in a dedicated namespace:
|
||||||
|
|
||||||
|
```bash tab="Install in a Dedicated Namespace"
|
||||||
|
# Install in the namespace "traefik-v2"
|
||||||
|
helm install --namespace=traefik-v2 \
|
||||||
|
./traefik-helm-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Installing with Custom Values"
|
||||||
|
|
||||||
|
You can customize the installation by specifying custom values,
|
||||||
|
as with [any helm chart](https://v2.helm.sh/docs/using_helm/#customizing-the-chart-before-installing).
|
||||||
|
{: #helm-custom-values }
|
||||||
|
|
||||||
|
The values are not (yet) documented, but are self-explanatory:
|
||||||
|
you can look at the [default `values.yaml`](https://github.com/containous/traefik-helm-chart/blob/master/values.yaml) file to explore possibilities.
|
||||||
|
|
||||||
|
Example of installation with logging set to `DEBUG`:
|
||||||
|
|
||||||
|
```bash tab="Using Helm CLI"
|
||||||
|
helm install --namespace=traefik-v2 \
|
||||||
|
--set="logs.loglevel=DEBUG" \
|
||||||
|
./traefik-helm-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
```yml tab="With a custom values file"
|
||||||
|
# File custom-values.yml
|
||||||
|
## Install with "helm install --values=./custom-values.yml ./traefik-helm-chart
|
||||||
|
logs:
|
||||||
|
loglevel: DEBUG
|
||||||
|
```
|
||||||
|
|
||||||
## Use the Binary Distribution
|
## Use the Binary Distribution
|
||||||
|
|
||||||
Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page.
|
Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page.
|
||||||
|
|
|
@ -47,11 +47,11 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.email="your-email@your-domain.org"
|
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
# used during the challenge
|
# used during the challenge
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
@ -156,8 +156,8 @@ when using the `HTTP-01` challenge, `certificatesResolvers.sample.acme.httpChall
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
@ -312,7 +312,7 @@ certificatesResolvers:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:="1.1.1.1:53,8.8.8.8:53"
|
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:=1.1.1.1:53,8.8.8.8:53
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Wildcard Domains
|
#### Wildcard Domains
|
||||||
|
@ -342,7 +342,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
|
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.email="test@traefik.io"
|
--certificatesResolvers.sample.acme.email=test@traefik.io
|
||||||
|
|
||||||
# File or key used for certificates storage.
|
# File or key used for certificates storage.
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
|
|
||||||
# CA server to use.
|
# CA server to use.
|
||||||
# Uncomment the line to use Let's Encrypt's staging server,
|
# Uncomment the line to use Let's Encrypt's staging server,
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
# Optional
|
# Optional
|
||||||
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
|
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
|
||||||
# KeyType to use.
|
# KeyType to use.
|
||||||
#
|
#
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
# Optional
|
# Optional
|
||||||
# Default: empty
|
# Default: empty
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers="1.1.1.1:53,8.8.8.8:53"
|
--certificatesResolvers.sample.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
||||||
|
|
||||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||||
#
|
#
|
||||||
|
|
|
@ -718,11 +718,11 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
--certificatesResolvers.sample.acme.email: your-email@your-domain.org
|
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||||
--certificatesResolvers.sample.acme.storage: acme.json
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint: web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
|
||||||
## Traefik Logs
|
## Traefik Logs
|
||||||
|
@ -744,9 +744,9 @@ There is no more log configuration at the root level.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--logLevel="DEBUG"
|
--logLevel=DEBUG
|
||||||
--traefikLog.filePath="/path/to/traefik.log"
|
--traefikLog.filePath=/path/to/traefik.log
|
||||||
--traefikLog.format="json"
|
--traefikLog.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -768,9 +768,9 @@ There is no more log configuration at the root level.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
--log.format="json"
|
--log.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tracing
|
## Tracing
|
||||||
|
@ -794,12 +794,12 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.backend="jaeger"
|
--tracing.backend=jaeger
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename=tracing
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||||
--tracing.jaeger.samplingparam="1.0"
|
--tracing.jaeger.samplingparam=1.0
|
||||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||||
--tracing.jaeger.samplingtype="const"
|
--tracing.jaeger.samplingtype=const
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -827,11 +827,11 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename=tracing
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||||
--tracing.jaeger.samplingparam="1.0"
|
--tracing.jaeger.samplingparam=1.0
|
||||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||||
--tracing.jaeger.samplingtype="const"
|
--tracing.jaeger.samplingtype=const
|
||||||
```
|
```
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
@ -852,7 +852,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||||
--metrics.prometheus.entrypoint="traefik"
|
--metrics.prometheus.entrypoint=traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -878,7 +878,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||||
--metrics.prometheus.entrypoint="metrics"
|
--metrics.prometheus.entrypoint=metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
## No More Root Level Key/Values
|
## No More Root Level Key/Values
|
||||||
|
@ -908,14 +908,14 @@ Each root item has been moved to a related section or removed.
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--checknewversion=false
|
--checknewversion=false
|
||||||
--sendanonymoususage=true
|
--sendanonymoususage=true
|
||||||
--loglevel="DEBUG"
|
--loglevel=DEBUG
|
||||||
--insecureskipverify=true
|
--insecureskipverify=true
|
||||||
--rootcas="/mycert.cert"
|
--rootcas=/mycert.cert
|
||||||
--maxidleconnsperhost=200
|
--maxidleconnsperhost=200
|
||||||
--providersthrottleduration="2s"
|
--providersthrottleduration=2s
|
||||||
--allowminweightzero=true
|
--allowminweightzero=true
|
||||||
--debug=true
|
--debug=true
|
||||||
--defaultentrypoints="web","web-secure"
|
--defaultentrypoints=web,web-secure
|
||||||
--keeptrailingslash=true
|
--keeptrailingslash=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -961,9 +961,9 @@ Each root item has been moved to a related section or removed.
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--global.checknewversion=true
|
--global.checknewversion=true
|
||||||
--global.sendanonymoususage=true
|
--global.sendanonymoususage=true
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
--serverstransport.insecureskipverify=true
|
--serverstransport.insecureskipverify=true
|
||||||
--serverstransport.rootcas="/mycert.cert"
|
--serverstransport.rootcas=/mycert.cert
|
||||||
--serverstransport.maxidleconnsperhost=42
|
--serverstransport.maxidleconnsperhost=42
|
||||||
--providers.providersthrottleduration=42
|
--providers.providersthrottleduration=42
|
||||||
```
|
```
|
||||||
|
|
|
@ -61,7 +61,7 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Configuring a buffer of 100 lines
|
# Configuring a buffer of 100 lines
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.bufferingsize=100
|
--accesslog.bufferingsize=100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -104,11 +104,11 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Configuring Multiple Filters
|
# Configuring Multiple Filters
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.format="json"
|
--accesslog.format=json
|
||||||
--accesslog.filters.statuscodes="200, 300-302"
|
--accesslog.filters.statuscodes=200,300-302
|
||||||
--accesslog.filters.retryattempts
|
--accesslog.filters.retryattempts
|
||||||
--accesslog.filters.minduration="10ms"
|
--accesslog.filters.minduration=10ms
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limiting the Fields
|
### Limiting the Fields
|
||||||
|
@ -164,14 +164,14 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Limiting the Logs to Specific Fields
|
# Limiting the Logs to Specific Fields
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.format="json"
|
--accesslog.format=json
|
||||||
--accesslog.fields.defaultmode="keep"
|
--accesslog.fields.defaultmode=keep
|
||||||
--accesslog.fields.names.ClientUsername="drop"
|
--accesslog.fields.names.ClientUsername=drop
|
||||||
--accesslog.fields.headers.defaultmode="keep"
|
--accesslog.fields.headers.defaultmode=keep
|
||||||
--accesslog.fields.headers.names.User-Agent="redact"
|
--accesslog.fields.headers.names.User-Agent=redact
|
||||||
--accesslog.fields.headers.names.Authorization="drop"
|
--accesslog.fields.headers.names.Authorization=drop
|
||||||
--accesslog.fields.headers.names.Content-Type="keep"
|
--accesslog.fields.headers.names.Content-Type=keep
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "Available Fields"
|
??? info "Available Fields"
|
||||||
|
|
|
@ -30,7 +30,7 @@ log:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Writing Logs to a File
|
# Writing Logs to a File
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `format`
|
#### `format`
|
||||||
|
@ -53,8 +53,8 @@ log:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Writing Logs to a File, in JSON
|
# Writing Logs to a File, in JSON
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
--log.format="json"
|
--log.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `level`
|
#### `level`
|
||||||
|
@ -72,7 +72,7 @@ log:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
```
|
```
|
||||||
|
|
||||||
## Log Rotation
|
## Log Rotation
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.datadog.address="127.0.0.1:8125"
|
--metrics.datadog.address=127.0.0.1:8125
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.address="localhost:8089"
|
--metrics.influxdb.address=localhost:8089
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `protocol`
|
#### `protocol`
|
||||||
|
@ -57,7 +57,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.protocol="udp"
|
--metrics.influxdb.protocol=udp
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `database`
|
#### `database`
|
||||||
|
@ -69,17 +69,17 @@ InfluxDB database used when protocol is http.
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
database = ""
|
database = "db"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
database: ""
|
database: "db"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.database=""
|
--metrics.influxdb.database=db
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `retentionPolicy`
|
#### `retentionPolicy`
|
||||||
|
@ -91,17 +91,17 @@ InfluxDB retention policy used when protocol is http.
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
retentionPolicy = ""
|
retentionPolicy = "two_hours"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
retentionPolicy: ""
|
retentionPolicy: "two_hours"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.retentionPolicy=""
|
--metrics.influxdb.retentionPolicy=two_hours
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `username`
|
#### `username`
|
||||||
|
@ -113,17 +113,17 @@ InfluxDB username (only with http).
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
username = ""
|
username = "john"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
username: ""
|
username: "john"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.username=""
|
--metrics.influxdb.username=john
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `password`
|
#### `password`
|
||||||
|
@ -135,17 +135,17 @@ InfluxDB password (only with http).
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
password = ""
|
password = "secret"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
password: ""
|
password: "secret"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.password=""
|
--metrics.influxdb.password=secret
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -113,8 +113,8 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.metrics.address=":8082"
|
--entryPoints.metrics.address=:8082
|
||||||
--metrics.prometheus.entryPoint="metrics"
|
--metrics.prometheus.entryPoint=metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `manualRouting`
|
#### `manualRouting`
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.statsd.address="localhost:8125"
|
--metrics.statsd.address=localhost:8125
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.datadog.localAgentHostPort="127.0.0.1:8126"
|
--tracing.datadog.localAgentHostPort=127.0.0.1:8126
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `debug`
|
#### `debug`
|
||||||
|
@ -79,7 +79,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.datadog.globalTag="sample"
|
--tracing.datadog.globalTag=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `prioritySampling`
|
#### `prioritySampling`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.localAgentHost="127.0.0.1"
|
--tracing.haystack.localAgentHost=127.0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentPort`
|
#### `localAgentPort`
|
||||||
|
@ -79,7 +79,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.globalTag="sample:test"
|
--tracing.haystack.globalTag=sample:test
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `traceIDHeaderName`
|
#### `traceIDHeaderName`
|
||||||
|
@ -101,7 +101,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.traceIDHeaderName="sample"
|
--tracing.haystack.traceIDHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `parentIDHeaderName`
|
#### `parentIDHeaderName`
|
||||||
|
@ -123,7 +123,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.parentIDHeaderName="sample"
|
--tracing.haystack.parentIDHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `spanIDHeaderName`
|
#### `spanIDHeaderName`
|
||||||
|
@ -168,5 +168,5 @@ tracing:
|
||||||
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.baggagePrefixHeaderName="sample"
|
--tracing.haystack.baggagePrefixHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.instana.localAgentHost="127.0.0.1"
|
--tracing.instana.localAgentHost=127.0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentPort`
|
#### `localAgentPort`
|
||||||
|
@ -86,5 +86,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.instana.logLevel="info"
|
--tracing.instana.logLevel=info
|
||||||
```
|
```
|
||||||
|
|
|
@ -39,7 +39,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingServerURL="http://localhost:5778/sampling"
|
--tracing.jaeger.samplingServerURL=http://localhost:5778/sampling
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `samplingType`
|
#### `samplingType`
|
||||||
|
@ -61,7 +61,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingType="const"
|
--tracing.jaeger.samplingType=const
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `samplingParam`
|
#### `samplingParam`
|
||||||
|
@ -89,7 +89,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingParam="1.0"
|
--tracing.jaeger.samplingParam=1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentHostPort`
|
#### `localAgentHostPort`
|
||||||
|
@ -111,7 +111,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.localAgentHostPort="127.0.0.1:6831"
|
--tracing.jaeger.localAgentHostPort=127.0.0.1:6831
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `gen128Bit`
|
#### `gen128Bit`
|
||||||
|
@ -159,7 +159,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.propagation="jaeger"
|
--tracing.jaeger.propagation=jaeger
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `traceContextHeaderName`
|
#### `traceContextHeaderName`
|
||||||
|
@ -182,7 +182,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.traceContextHeaderName="uber-trace-id"
|
--tracing.jaeger.traceContextHeaderName=uber-trace-id
|
||||||
```
|
```
|
||||||
|
|
||||||
### `collector`
|
### `collector`
|
||||||
|
@ -206,7 +206,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.collector.endpoint="http://127.0.0.1:14268/api/traces?format=jaeger.thrift"
|
--tracing.jaeger.collector.endpoint=http://127.0.0.1:14268/api/traces?format=jaeger.thrift
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `user`
|
#### `user`
|
||||||
|
@ -229,7 +229,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.collector.user="my-user"
|
--tracing.jaeger.collector.user=my-user
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `password`
|
#### `password`
|
||||||
|
@ -252,5 +252,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.collector.password="my-password"
|
--tracing.jaeger.collector.password=my-password
|
||||||
```
|
```
|
||||||
|
|
|
@ -52,7 +52,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.serviceName="traefik"
|
--tracing.serviceName=traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `spanNameLimit`
|
#### `spanNameLimit`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.zipkin.httpEndpoint="http://localhost:9411/api/v2/spans"
|
--tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `sameSpan`
|
#### `sameSpan`
|
||||||
|
@ -101,5 +101,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.zipkin.sampleRate="0.2"
|
--tracing.zipkin.sampleRate=0.2
|
||||||
```
|
```
|
||||||
|
|
|
@ -55,8 +55,8 @@ ping:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.ping.address=":8082"
|
--entryPoints.ping.address=:8082
|
||||||
--ping.entryPoint="ping"
|
--ping.entryPoint=ping
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `manualRouting`
|
#### `manualRouting`
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Traefik & Consul Catalog
|
# Traefik & Consul Catalog
|
||||||
|
|
||||||
A Story of Labels, Services & Containers
|
A Story of Tags, Services & Instances
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
![Consul Catalog](../assets/img/providers/consul.png)
|
![Consul Catalog](../assets/img/providers/consul.png)
|
||||||
|
|
||||||
Attach labels to your services and let Traefik do the rest!
|
Attach tags to your services and let Traefik do the rest!
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
|
@ -26,10 +26,9 @@ Attach labels to your services and let Traefik do the rest!
|
||||||
--providers.consulcatalog=true
|
--providers.consulcatalog=true
|
||||||
```
|
```
|
||||||
|
|
||||||
Attaching labels to services
|
Attaching tags to services
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
|
||||||
- traefik.http.services.my-service.rule=Host(`mydomain.com`)
|
- traefik.http.services.my-service.rule=Host(`mydomain.com`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -65,27 +64,27 @@ Defines the polling interval.
|
||||||
|
|
||||||
### `prefix`
|
### `prefix`
|
||||||
|
|
||||||
_Optional, Default=/latest_
|
_required, Default="traefik"_
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.consulCatalog]
|
[providers.consulCatalog]
|
||||||
prefix = "/test"
|
prefix = "test"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
consulCatalog:
|
consulCatalog:
|
||||||
prefix: /test
|
prefix: test
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.consulcatalog.prefix=/test
|
--providers.consulcatalog.prefix=test
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Prefix used for accessing the Consul service metadata.
|
The prefix for Consul Catalog tags defining traefik labels.
|
||||||
|
|
||||||
### `requireConsistent`
|
### `requireConsistent`
|
||||||
|
|
||||||
|
@ -161,7 +160,7 @@ Use local agent caching for catalog reads.
|
||||||
|
|
||||||
### `endpoint`
|
### `endpoint`
|
||||||
|
|
||||||
Defines Consul server endpoint.
|
Defines the Consul server endpoint.
|
||||||
|
|
||||||
#### `address`
|
#### `address`
|
||||||
|
|
||||||
|
@ -504,7 +503,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
Expose Consul Catalog services by default in Traefik.
|
Expose Consul Catalog services by default in Traefik.
|
||||||
If set to false, services that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
|
If set to false, services that don't have a `traefik.enable=true` tag will be ignored from the resulting routing configuration.
|
||||||
|
|
||||||
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||||
|
|
||||||
|
@ -532,13 +531,13 @@ providers:
|
||||||
|
|
||||||
The default host rule for all services.
|
The default host rule for all services.
|
||||||
|
|
||||||
For a given container if no routing rule was defined by a label, it is defined by this defaultRule instead.
|
For a given service if no routing rule was defined by a tag, it is defined by this defaultRule instead.
|
||||||
It must be a valid [Go template](https://golang.org/pkg/text/template/),
|
It must be a valid [Go template](https://golang.org/pkg/text/template/),
|
||||||
augmented with the [sprig template functions](http://masterminds.github.io/sprig/).
|
augmented with the [sprig template functions](http://masterminds.github.io/sprig/).
|
||||||
The service name can be accessed as the `Name` identifier,
|
The service name can be accessed as the `Name` identifier,
|
||||||
and the template has access to all the labels defined on this container.
|
and the template has access to all the labels (i.e. tags beginning with the `prefix`) defined on this service.
|
||||||
|
|
||||||
This option can be overridden on a container basis with the `traefik.http.routers.Router1.rule` label.
|
The option can be overridden on an instance basis with the `traefik.http.routers.{name-of-your-choice}.rule` tag.
|
||||||
|
|
||||||
### `constraints`
|
### `constraints`
|
||||||
|
|
||||||
|
@ -546,58 +545,59 @@ _Optional, Default=""_
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.consulCatalog]
|
[providers.consulCatalog]
|
||||||
constraints = "Label(`a.label.name`, `foo`)"
|
constraints = "Tag(`a.tag.name`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
consulCatalog:
|
consulCatalog:
|
||||||
constraints: "Label(`a.label.name`, `foo`)"
|
constraints: "Tag(`a.tag.name`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.consulcatalog.constraints="Label(`a.label.name`, `foo`)"
|
--providers.consulcatalog.constraints="Tag(`a.tag.name`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
Constraints is an expression that Traefik matches against the service's tags to determine whether to create any route for that service.
|
||||||
That is to say, if none of the container's labels match the expression, no route for the container is created.
|
That is to say, if none of the service's tags match the expression, no route for that service is created.
|
||||||
If the expression is empty, all detected containers are included.
|
If the expression is empty, all detected services are included.
|
||||||
|
|
||||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below.
|
The expression syntax is based on the `Tag("tag")`, and `TagRegex("tag")` functions,
|
||||||
|
as well as the usual boolean logic, as shown in examples below.
|
||||||
|
|
||||||
??? example "Constraints Expression Examples"
|
??? example "Constraints Expression Examples"
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Includes only containers having a label with key `a.label.name` and value `foo`
|
# Includes only services having the tag `a.tag.name=foo`
|
||||||
constraints = "Label(`a.label.name`, `foo`)"
|
constraints = "Tag(`a.tag.name=foo`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
# Excludes services having any tag `a.tag.name=foo`
|
||||||
constraints = "!Label(`a.label.name`, `value`)"
|
constraints = "!Tag(`a.tag.name=foo`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# With logical AND.
|
# With logical AND.
|
||||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
constraints = "Tag(`a.tag.name`) && Tag(`another.tag.name`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# With logical OR.
|
# With logical OR.
|
||||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
constraints = "Tag(`a.tag.name`) || Tag(`another.tag.name`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# With logical AND and OR, with precedence set by parentheses.
|
# 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`))"
|
constraints = "Tag(`a.tag.name`) && (Tag(`another.tag.name`) || Tag(`yet.another.tag.name`))"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
# Includes only services having a tag matching the `a\.tag\.t.+` regular expression.
|
||||||
constraints = "LabelRegex(`a.label.name`, `a.+`)"
|
constraints = "TagRegex(`a\.tag\.t.+`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||||
|
|
|
@ -7,6 +7,9 @@ A Story of Labels & Containers
|
||||||
|
|
||||||
Attach labels to your containers and let Traefik do the rest!
|
Attach labels to your containers and let Traefik do the rest!
|
||||||
|
|
||||||
|
Traefik works with both [Docker (standalone) Engine](https://docs.docker.com/engine/)
|
||||||
|
and [Docker Swarm Mode](https://docs.docker.com/engine/swarm/).
|
||||||
|
|
||||||
!!! tip "The Quick Start Uses Docker"
|
!!! tip "The Quick Start Uses Docker"
|
||||||
If you haven't already, maybe you'd like to go through the [quick start](../getting-started/quick-start.md) that uses the docker provider!
|
If you haven't already, maybe you'd like to go through the [quick start](../getting-started/quick-start.md) that uses the docker provider!
|
||||||
|
|
||||||
|
@ -64,7 +67,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
--providers.docker.endpoint=tcp://127.0.0.1:2375
|
||||||
--providers.docker.swarmMode=true
|
--providers.docker.swarmMode=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -80,15 +83,136 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
- traefik.http.services.my-container-service.loadbalancer.server.port=8080
|
- traefik.http.services.my-container-service.loadbalancer.server.port=8080
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! important "Labels in Docker Swarm Mode"
|
|
||||||
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
|
|
||||||
|
|
||||||
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the `deploy` part of your service.
|
|
||||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
|
|
||||||
|
|
||||||
## Routing Configuration
|
## Routing Configuration
|
||||||
|
|
||||||
See the dedicated section in [routing](../routing/providers/docker.md).
|
When using Docker as a [provider](https://docs.traefik.io/providers/overview/),
|
||||||
|
Trafik uses [container labels](https://docs.docker.com/engine/reference/commandline/run/#set-metadata-on-container--l---label---label-file) to retrieve its routing configuration.
|
||||||
|
|
||||||
|
See the list of labels in the dedicated [routing](../routing/providers/docker.md) section.
|
||||||
|
|
||||||
|
### Routing Configuration with Labels
|
||||||
|
|
||||||
|
By default, Traefik watches for [container level labels](https://docs.docker.com/config/labels-custom-metadata/) on a standalone Docker Engine.
|
||||||
|
|
||||||
|
When using Docker Compose, labels are specified by the directive
|
||||||
|
[`labels`](https://docs.docker.com/compose/compose-file/#labels) from the
|
||||||
|
["services" objects](https://docs.docker.com/compose/compose-file/#service-configuration-reference).
|
||||||
|
|
||||||
|
!!! tip "Not Only Docker"
|
||||||
|
Please note that any tool like Nomad, Terraform, Ansible, etc.
|
||||||
|
that is able to define a Docker container with labels can work
|
||||||
|
with Traefik & the Docker provider.
|
||||||
|
|
||||||
|
### Port Detection
|
||||||
|
|
||||||
|
Traefik retrieves the private IP and port of containers from the Docker API.
|
||||||
|
|
||||||
|
Ports detection works as follows:
|
||||||
|
|
||||||
|
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) only one port,
|
||||||
|
then Traefik uses this port for private communication.
|
||||||
|
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) multiple ports,
|
||||||
|
or does not expose any port, then you must manually specify which port Traefik should use for communication
|
||||||
|
by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
|
||||||
|
(Read more on this label in the dedicated section in [routing](../routing/providers/docker.md#port)).
|
||||||
|
|
||||||
|
### Docker API Access
|
||||||
|
|
||||||
|
Traefik requires access to the docker socket to get its dynamic configuration.
|
||||||
|
|
||||||
|
You can specify which Docker API Endpoint to use with the directive [`endpoint`](#endpoint).
|
||||||
|
|
||||||
|
!!! warning "Security Note"
|
||||||
|
|
||||||
|
Accessing the Docker API without any restriction is a security concern:
|
||||||
|
If Traefik is attacked, then the attacker might get access to the underlying host.
|
||||||
|
{: #security-note }
|
||||||
|
|
||||||
|
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
|
||||||
|
|
||||||
|
!!! quote
|
||||||
|
[...] only **trusted** users should be allowed to control your Docker daemon [...]
|
||||||
|
|
||||||
|
??? success "Solutions"
|
||||||
|
|
||||||
|
Expose the Docker socket over TCP, instead of the default Unix socket file.
|
||||||
|
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
|
||||||
|
|
||||||
|
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
|
||||||
|
- Authorize and filter requests to restrict possible actions with [the TecnativaDocker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy).
|
||||||
|
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
|
||||||
|
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
|
||||||
|
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
||||||
|
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
||||||
|
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
||||||
|
|
||||||
|
??? info "More Resources and Examples"
|
||||||
|
- ["Paranoid about mounting /var/run/docker.sock?"](https://medium.com/@containeroo/traefik-2-0-paranoid-about-mounting-var-run-docker-sock-22da9cb3e78c)
|
||||||
|
- [Traefik and Docker: A Discussion with Docker Captain, Bret Fisher](https://blog.containo.us/traefik-and-docker-a-discussion-with-docker-captain-bret-fisher-7f0b9a54ff88)
|
||||||
|
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
||||||
|
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
||||||
|
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||||
|
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
||||||
|
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
|
||||||
|
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
|
||||||
|
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
|
||||||
|
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
|
||||||
|
|
||||||
|
## Docker Swarm Mode
|
||||||
|
|
||||||
|
To enable Docker Swarm (instead of standalone Docker) as a configuration provider,
|
||||||
|
set the [`swarmMode`](#swarmmode) directive to `true`.
|
||||||
|
|
||||||
|
### Routing Configuration with Labels
|
||||||
|
|
||||||
|
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
|
||||||
|
|
||||||
|
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the
|
||||||
|
[`deploy`](https://docs.docker.com/compose/compose-file/#labels-1) part of your service.
|
||||||
|
|
||||||
|
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file)).
|
||||||
|
|
||||||
|
### Port Detection
|
||||||
|
|
||||||
|
Docker Swarm does not provide any [port detection](#port-detection) information to Traefik.
|
||||||
|
|
||||||
|
Therefore you **must** specify the port to use for communication by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
|
||||||
|
(Check the reference for this label in the [routing section for Docker](../routing/providers/docker.md#port)).
|
||||||
|
|
||||||
|
### Docker API Access
|
||||||
|
|
||||||
|
Docker Swarm Mode follows the same rules as Docker [API Access](#docker-api-access).
|
||||||
|
|
||||||
|
As the Swarm API is only exposed on the [manager nodes](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/#manager-nodes), you should schedule Traefik on the Swarm manager nodes by default,
|
||||||
|
by deploying Traefik with a [constraint](https://success.docker.com/article/using-contraints-and-labels-to-control-the-placement-of-containers) on the node's "role":
|
||||||
|
|
||||||
|
```shell tab="With Docker CLI"
|
||||||
|
docker service create \
|
||||||
|
--constraint=node.role==manager \
|
||||||
|
#... \
|
||||||
|
```
|
||||||
|
|
||||||
|
```yml tab="With Docker Compose"
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
# ...
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "Scheduling Traefik on Worker Nodes"
|
||||||
|
|
||||||
|
Following the guidelines given in the previous section ["Docker API Access"](#docker-api-access),
|
||||||
|
if you expose the Docker API through TCP, then Traefik can be scheduled on any node if the TCP
|
||||||
|
socket is reachable.
|
||||||
|
|
||||||
|
Please consider the security implications by reading the [Security Note](#security-note).
|
||||||
|
|
||||||
|
A good example can be found on [Bret Fisher's repository](https://github.com/BretFisher/dogvscat/blob/master/stack-proxy-global.yml#L124).
|
||||||
|
|
||||||
## Provider Configuration
|
## Provider Configuration
|
||||||
|
|
||||||
|
@ -108,51 +232,10 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.endpoint="unix:///var/run/docker.sock"
|
--providers.docker.endpoint=unix:///var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
Traefik requires access to the docker socket to get its dynamic configuration.
|
See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API Access](#docker-api-access_1) for more information.
|
||||||
|
|
||||||
??? warning "Security Notes"
|
|
||||||
|
|
||||||
Depending on your context, accessing the Docker API without any restriction can be a security concern: If Traefik is attacked, then the attacker might get access to the Docker (or Swarm Mode) backend.
|
|
||||||
|
|
||||||
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
|
|
||||||
|
|
||||||
`[...] only **trusted** users should be allowed to control your Docker daemon [...]`
|
|
||||||
|
|
||||||
!!! tip "Improved Security"
|
|
||||||
|
|
||||||
[TraefikEE](https://containo.us/traefikee) solves this problem by separating the control plane (connected to Docker) and the data plane (handling the requests).
|
|
||||||
|
|
||||||
??? info "Resources about Docker's Security"
|
|
||||||
|
|
||||||
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
|
||||||
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
|
||||||
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
|
||||||
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
|
||||||
|
|
||||||
??? tip "Security Compensation"
|
|
||||||
|
|
||||||
Expose the Docker socket over TCP, instead of the default Unix socket file.
|
|
||||||
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
|
|
||||||
|
|
||||||
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
|
|
||||||
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
|
|
||||||
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
|
|
||||||
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
|
||||||
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
|
||||||
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
|
||||||
|
|
||||||
??? info "Additional Resources"
|
|
||||||
|
|
||||||
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
|
|
||||||
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
|
|
||||||
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
|
|
||||||
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
|
|
||||||
|
|
||||||
!!! info "Traefik & Swarm Mode"
|
|
||||||
To let Traefik access the Docker Socket of the Swarm manager, it is mandatory to schedule Traefik on the Swarm manager nodes.
|
|
||||||
|
|
||||||
??? example "Using the docker.sock"
|
??? example "Using the docker.sock"
|
||||||
|
|
||||||
|
@ -186,7 +269,7 @@ Traefik requires access to the docker socket to get its dynamic configuration.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.endpoint="unix:///var/run/docker.sock"
|
--providers.docker.endpoint=unix:///var/run/docker.sock
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -311,7 +394,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -343,7 +426,7 @@ providers:
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Activates the Swarm Mode.
|
Activates the Swarm Mode (instead of standalone Docker).
|
||||||
|
|
||||||
### `swarmModeRefreshSeconds`
|
### `swarmModeRefreshSeconds`
|
||||||
|
|
||||||
|
@ -387,7 +470,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.constraints="Label(`a.label.name`, `foo`)"
|
--providers.docker.constraints=Label(`a.label.name`,`foo`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.endpoint="http://localhost:8080"
|
--providers.kubernetescrd.endpoint=http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
The Kubernetes server endpoint as URL.
|
The Kubernetes server endpoint as URL.
|
||||||
|
@ -66,7 +66,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.token="mytoken"
|
--providers.kubernetescrd.token=mytoken
|
||||||
```
|
```
|
||||||
|
|
||||||
Bearer token used for the Kubernetes client configuration.
|
Bearer token used for the Kubernetes client configuration.
|
||||||
|
@ -89,7 +89,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.certauthfilepath="/my/ca.crt"
|
--providers.kubernetescrd.certauthfilepath=/my/ca.crt
|
||||||
```
|
```
|
||||||
|
|
||||||
Path to the certificate authority file.
|
Path to the certificate authority file.
|
||||||
|
@ -115,7 +115,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.namespaces="default,production"
|
--providers.kubernetescrd.namespaces=default,production
|
||||||
```
|
```
|
||||||
|
|
||||||
Array of namespaces to watch.
|
Array of namespaces to watch.
|
||||||
|
@ -164,7 +164,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.ingressclass="traefik-internal"
|
--providers.kubernetescrd.ingressclass=traefik-internal
|
||||||
```
|
```
|
||||||
|
|
||||||
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||||
|
@ -190,7 +190,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.throttleDuration="10s"
|
--providers.kubernetescrd.throttleDuration=10s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
|
@ -67,7 +67,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.endpoint="http://localhost:8080"
|
--providers.kubernetesingress.endpoint=http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
The Kubernetes server endpoint as URL, which is only used when the behavior based on environment variables described below does not apply.
|
The Kubernetes server endpoint as URL, which is only used when the behavior based on environment variables described below does not apply.
|
||||||
|
@ -99,7 +99,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.token="mytoken"
|
--providers.kubernetesingress.token=mytoken
|
||||||
```
|
```
|
||||||
|
|
||||||
Bearer token used for the Kubernetes client configuration.
|
Bearer token used for the Kubernetes client configuration.
|
||||||
|
@ -122,7 +122,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.certauthfilepath="/my/ca.crt"
|
--providers.kubernetesingress.certauthfilepath=/my/ca.crt
|
||||||
```
|
```
|
||||||
|
|
||||||
Path to the certificate authority file.
|
Path to the certificate authority file.
|
||||||
|
@ -171,7 +171,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.namespaces="default,production"
|
--providers.kubernetesingress.namespaces=default,production
|
||||||
```
|
```
|
||||||
|
|
||||||
Array of namespaces to watch.
|
Array of namespaces to watch.
|
||||||
|
@ -220,7 +220,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressclass="traefik-internal"
|
--providers.kubernetesingress.ingressclass=traefik-internal
|
||||||
```
|
```
|
||||||
|
|
||||||
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||||
|
@ -249,7 +249,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressendpoint.hostname="foo.com"
|
--providers.kubernetesingress.ingressendpoint.hostname=foo.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Hostname used for Kubernetes Ingress endpoints.
|
Hostname used for Kubernetes Ingress endpoints.
|
||||||
|
@ -273,7 +273,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressendpoint.ip="1.2.3.4"
|
--providers.kubernetesingress.ingressendpoint.ip=1.2.3.4
|
||||||
```
|
```
|
||||||
|
|
||||||
IP used for Kubernetes Ingress endpoints.
|
IP used for Kubernetes Ingress endpoints.
|
||||||
|
@ -297,7 +297,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressendpoint.publishedservice="foo-service"
|
--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service
|
||||||
```
|
```
|
||||||
|
|
||||||
Published Kubernetes Service to copy status from.
|
Published Kubernetes Service to copy status from.
|
||||||
|
@ -320,7 +320,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.throttleDuration="10s"
|
--providers.kubernetesingress.throttleDuration=10s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
|
@ -74,8 +74,8 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.basic.httpbasicauthuser="foo"
|
--providers.marathon.basic.httpbasicauthuser=foo
|
||||||
--providers.marathon.basic.httpbasicpassword="bar"
|
--providers.marathon.basic.httpbasicpassword=bar
|
||||||
```
|
```
|
||||||
|
|
||||||
Enables Marathon basic authentication.
|
Enables Marathon basic authentication.
|
||||||
|
@ -98,7 +98,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.dcosToken="xxxxxx"
|
--providers.marathon.dcosToken=xxxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
DCOSToken for DCOS environment.
|
DCOSToken for DCOS environment.
|
||||||
|
@ -123,7 +123,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
--providers.marathon.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.endpoint="http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
--providers.marathon.endpoint=http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
Marathon server endpoint.
|
Marathon server endpoint.
|
||||||
|
@ -235,7 +235,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.constraints="Label(`a.label.name`, `foo`)"
|
--providers.marathon.constraints=Label(`a.label.name`,`foo`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.responseHeaderTimeout="66s"
|
--providers.marathon.responseHeaderTimeout=66s
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.responseHeaderTimeout="10s"
|
--providers.marathon.responseHeaderTimeout=10s
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.rancher.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
--providers.rancher.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.rancher.prefix="/test"
|
--providers.rancher.prefix=/test
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.rancher.constraints="Label(`a.label.name`, `foo`)"
|
--providers.rancher.constraints=Label(`a.label.name`,`foo`)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,4 @@
|
||||||
--providers.rancher.intervalPoll=false
|
--providers.rancher.intervalPoll=false
|
||||||
|
|
||||||
# Prefix used for accessing the Rancher metadata service
|
# Prefix used for accessing the Rancher metadata service
|
||||||
--providers.rancher.prefix="/latest"
|
--providers.rancher.prefix=/latest
|
||||||
|
|
|
@ -18,4 +18,4 @@ providers:
|
||||||
intervalPoll: false
|
intervalPoll: false
|
||||||
|
|
||||||
# Prefix used for accessing the Rancher metadata service
|
# Prefix used for accessing the Rancher metadata service
|
||||||
prefix: "/latest"
|
prefix: /latest
|
||||||
|
|
|
@ -128,9 +128,9 @@ You can define them using a toml file, CLI arguments, or a key-value store.
|
||||||
--entryPoints.name.transport.respondingTimeouts.writeTimeout=42
|
--entryPoints.name.transport.respondingTimeouts.writeTimeout=42
|
||||||
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
|
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
|
||||||
--entryPoints.name.proxyProtocol.insecure=true
|
--entryPoints.name.proxyProtocol.insecure=true
|
||||||
--entryPoints.name.proxyProtocol.trustedIPs="127.0.0.1,192.168.0.1"
|
--entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1
|
||||||
--entryPoints.name.forwardedHeaders.insecure=true
|
--entryPoints.name.forwardedHeaders.insecure=true
|
||||||
--entryPoints.name.forwardedHeaders.trustedIPs="127.0.0.1,192.168.0.1"
|
--entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Forwarded Header
|
### Forwarded Header
|
||||||
|
|
|
@ -151,7 +151,7 @@ http:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Listen on port 8081 for incoming requests
|
# Listen on port 8081 for incoming requests
|
||||||
--entryPoints.web.address=":8081"
|
--entryPoints.web.address=:8081
|
||||||
|
|
||||||
# Enable the file provider to define routers / middlewares / services in a file
|
# Enable the file provider to define routers / middlewares / services in a file
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--providers.file.filename=dynamic_conf.toml
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
# Traefik & Consul Catalog
|
# Traefik & Consul Catalog
|
||||||
|
|
||||||
A Story of Labels, Services & Containers
|
A Story of Tags, Services & Instances
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
![Rancher](../../assets/img/providers/consul.png)
|
![Rancher](../../assets/img/providers/consul.png)
|
||||||
|
|
||||||
Attach labels to your services and let Traefik do the rest!
|
Attach tags to your services and let Traefik do the rest!
|
||||||
|
|
||||||
## Routing Configuration
|
## Routing Configuration
|
||||||
|
|
||||||
!!! info "Labels"
|
!!! info "tags"
|
||||||
|
|
||||||
- Labels are case insensitive.
|
- tags are case insensitive.
|
||||||
- The complete list of labels can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md)
|
- The complete list of tags can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md)
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
Traefik creates, for each consul Catalog service, a corresponding [service](../services/index.md) and [router](../routers/index.md).
|
Traefik creates, for each consul Catalog service, a corresponding [service](../services/index.md) and [router](../routers/index.md).
|
||||||
|
|
||||||
The Service automatically gets a server per container in this consul Catalog service, and the router gets a default rule attached to it, based on the service name.
|
The Service automatically gets a server per instance in this consul Catalog service, and the router gets a default rule attached to it, based on the service name.
|
||||||
|
|
||||||
### Routers
|
### Routers
|
||||||
|
|
||||||
To update the configuration of the Router automatically attached to the container, add labels starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change.
|
To update the configuration of the Router automatically attached to the service, add tags starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change.
|
||||||
|
|
||||||
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
|
For example, to change the rule, you could add the tag ```traefik.http.routers.my-service.rule=Host(`mydomain.com`)```.
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#rule) for more information.
|
See [rule](../routers/index.md#rule) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.rule=Host(`mydomain.com`)"
|
traefik.http.routers.myrouter.rule=Host(`mydomain.com`)
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
||||||
|
@ -39,7 +39,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
traefik.http.routers.myrouter.entrypoints=web,websecure
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||||
|
@ -47,7 +47,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
|
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.middlewares=auth,prefix,cb"
|
traefik.http.routers.myrouter.middlewares=auth,prefix,cb
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.service`"
|
??? info "`traefik.http.routers.<router_name>.service`"
|
||||||
|
@ -55,7 +55,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [rule](../routers/index.md#service) for more information.
|
See [rule](../routers/index.md#service) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.service=myservice"
|
traefik.http.routers.myrouter.service=myservice
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls`"
|
??? info "`traefik.http.routers.<router_name>.tls`"
|
||||||
|
@ -63,7 +63,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [tls](../routers/index.md#tls) for more information.
|
See [tls](../routers/index.md#tls) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter>.tls=true"
|
traefik.http.routers.myrouter>.tls=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
||||||
|
@ -71,7 +71,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [certResolver](../routers/index.md#certresolver) for more information.
|
See [certResolver](../routers/index.md#certresolver) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.certresolver=myresolver"
|
traefik.http.routers.myrouter.tls.certresolver=myresolver
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
||||||
|
@ -79,7 +79,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [domains](../routers/index.md#domains) for more information.
|
See [domains](../routers/index.md#domains) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.domains[0].main=foobar.com"
|
traefik.http.routers.myrouter.tls.domains[0].main=foobar.com
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
||||||
|
@ -87,7 +87,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [domains](../routers/index.md#domains) for more information.
|
See [domains](../routers/index.md#domains) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
||||||
|
@ -95,31 +95,31 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [options](../routers/index.md#options) for more information.
|
See [options](../routers/index.md#options) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.options=foobar"
|
traefik.http.routers.myrouter.tls.options=foobar
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||||
<!-- TODO doc priority in routers page -->
|
<!-- TODO doc priority in routers page -->
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.priority=42"
|
traefik.http.routers.myrouter.priority=42
|
||||||
```
|
```
|
||||||
|
|
||||||
### Services
|
### Services
|
||||||
|
|
||||||
To update the configuration of the Service automatically attached to the container,
|
To update the configuration of the Service automatically attached to the service,
|
||||||
add labels starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change.
|
add tags starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change.
|
||||||
|
|
||||||
For example, to change the `passHostHeader` behavior,
|
For example, to change the `passHostHeader` behavior,
|
||||||
you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||||
|
|
||||||
Registers a port.
|
Registers a port.
|
||||||
Useful when the container exposes multiples ports.
|
Useful when the service exposes multiples ports.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
traefik.http.services.myservice.loadbalancer.server.port=8080
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
||||||
|
@ -127,14 +127,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
Overrides the default scheme.
|
Overrides the default scheme.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"
|
traefik.http.services.myservice.loadbalancer.server.scheme=http
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
||||||
<!-- TODO doc passHostHeader in services page -->
|
<!-- TODO doc passHostHeader in services page -->
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.passhostheader=true"
|
traefik.http.services.myservice.loadbalancer.passhostheader=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
||||||
|
@ -142,7 +142,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar"
|
traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
|
||||||
|
@ -150,7 +150,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com"
|
traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
|
||||||
|
@ -158,7 +158,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10"
|
traefik.http.services.myservice.loadbalancer.healthcheck.interval=10
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
|
||||||
|
@ -166,7 +166,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo"
|
traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
|
||||||
|
@ -174,7 +174,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.port=42"
|
traefik.http.services.myservice.loadbalancer.healthcheck.port=42
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
|
||||||
|
@ -182,7 +182,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http"
|
traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
|
||||||
|
@ -190,7 +190,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
@ -198,7 +198,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky=true"
|
traefik.http.services.myservice.loadbalancer.sticky=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
|
||||||
|
@ -206,7 +206,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true"
|
traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
|
||||||
|
@ -214,7 +214,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar"
|
traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
|
||||||
|
@ -222,7 +222,7 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
|
traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||||
|
@ -231,12 +231,12 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
FlushInterval specifies the flush interval to flush to the client while copying the response body.
|
FlushInterval specifies the flush interval to flush to the client while copying the response body.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10"
|
traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10
|
||||||
```
|
```
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
|
You can declare pieces of middleware using tags starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
|
||||||
|
|
||||||
For example, to declare a middleware [`redirectscheme`](../../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`.
|
For example, to declare a middleware [`redirectscheme`](../../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`.
|
||||||
|
|
||||||
|
@ -246,11 +246,10 @@ More information about available middlewares in the dedicated [middlewares secti
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ...
|
# ...
|
||||||
labels:
|
|
||||||
# Declaring a middleware
|
# Declaring a middleware
|
||||||
- traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
|
traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
|
||||||
# Referencing a middleware
|
# Referencing a middleware
|
||||||
- traefik.http.routers.my-container.middlewares=my-redirect
|
traefik.http.routers.my-service.middlewares=my-redirect
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! warning "Conflicts in Declaration"
|
!!! warning "Conflicts in Declaration"
|
||||||
|
@ -259,24 +258,20 @@ More information about available middlewares in the dedicated [middlewares secti
|
||||||
|
|
||||||
### TCP
|
### TCP
|
||||||
|
|
||||||
You can declare TCP Routers and/or Services using labels.
|
You can declare TCP Routers and/or Services using tags.
|
||||||
|
|
||||||
??? example "Declaring TCP Routers and Services"
|
??? example "Declaring TCP Routers and Services"
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
traefik.tcp.routers.my-router.rule=HostSNI(`my-host.com`)
|
||||||
my-container:
|
traefik.tcp.routers.my-router.tls=true
|
||||||
# ...
|
traefik.tcp.services.my-service.loadbalancer.server.port=4123
|
||||||
labels:
|
|
||||||
- "traefik.tcp.routers.my-router.rule=HostSNI(`my-host.com`)"
|
|
||||||
- "traefik.tcp.routers.my-router.tls=true"
|
|
||||||
- "traefik.tcp.services.my-service.loadbalancer.server.port=4123"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! warning "TCP and HTTP"
|
!!! warning "TCP and HTTP"
|
||||||
|
|
||||||
If you declare a TCP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no TCP Router/Service is defined).
|
If you declare a TCP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no TCP Router/Service is defined).
|
||||||
You can declare both a TCP Router/Service and an HTTP Router/Service for the same container (but you have to do so manually).
|
You can declare both a TCP Router/Service and an HTTP Router/Service for the same consul service (but you have to do so manually).
|
||||||
|
|
||||||
#### TCP Routers
|
#### TCP Routers
|
||||||
|
|
||||||
|
@ -285,7 +280,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2"
|
traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
||||||
|
@ -293,7 +288,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [rule](../routers/index.md#rule_1) for more information.
|
See [rule](../routers/index.md#rule_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)"
|
traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.service`"
|
??? info "`traefik.tcp.routers.<router_name>.service`"
|
||||||
|
@ -301,7 +296,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [service](../routers/index.md#services) for more information.
|
See [service](../routers/index.md#services) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.service=myservice"
|
traefik.tcp.routers.mytcprouter.service=myservice
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
||||||
|
@ -309,7 +304,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [TLS](../routers/index.md#tls_1) for more information.
|
See [TLS](../routers/index.md#tls_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls=true"
|
traefik.tcp.routers.mytcprouter.tls=true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
||||||
|
@ -317,7 +312,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver"
|
traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
||||||
|
@ -325,7 +320,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [domains](../routers/index.md#domains_1) for more information.
|
See [domains](../routers/index.md#domains_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com"
|
traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
||||||
|
@ -333,7 +328,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [domains](../routers/index.md#domains_1) for more information.
|
See [domains](../routers/index.md#domains_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
||||||
|
@ -341,7 +336,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [options](../routers/index.md#options_1) for more information.
|
See [options](../routers/index.md#options_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.options=mysoptions"
|
traefik.tcp.routers.mytcprouter.tls.options=mysoptions
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
|
||||||
|
@ -349,7 +344,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [TLS](../routers/index.md#tls_1) for more information.
|
See [TLS](../routers/index.md#tls_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.passthrough=true"
|
traefik.tcp.routers.mytcprouter.tls.passthrough=true
|
||||||
```
|
```
|
||||||
|
|
||||||
#### TCP Services
|
#### TCP Services
|
||||||
|
@ -359,7 +354,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
Registers a port of the application.
|
Registers a port of the application.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.server.port=423"
|
traefik.tcp.services.mytcpservice.loadbalancer.server.port=423
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
||||||
|
@ -367,7 +362,7 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
See [termination delay](../services/index.md#termination-delay) for more information.
|
See [termination delay](../services/index.md#termination-delay) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
||||||
```
|
```
|
||||||
|
|
||||||
### Specific Provider Options
|
### Specific Provider Options
|
||||||
|
@ -375,10 +370,10 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
#### `traefik.enable`
|
#### `traefik.enable`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.enable=true"
|
traefik.enable=true
|
||||||
```
|
```
|
||||||
|
|
||||||
You can tell Traefik to consider (or not) the container by setting `traefik.enable` to true or false.
|
You can tell Traefik to consider (or not) the service by setting `traefik.enable` to true or false.
|
||||||
|
|
||||||
This option overrides the value of `exposedByDefault`.
|
This option overrides the value of `exposedByDefault`.
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
--providers.docker.endpoint=tcp://127.0.0.1:2375
|
||||||
--providers.docker.swarmMode=true
|
--providers.docker.swarmMode=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||||
|
@ -247,7 +247,8 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
||||||
Registers a port.
|
Registers a port.
|
||||||
Useful when the container exposes multiples ports.
|
Useful when the container exposes multiples ports.
|
||||||
|
|
||||||
Mandatory for Docker Swarm.
|
Mandatory for Docker Swarm (see the section ["Port Detection with Docker Swarm"](../../providers/docker.md#port-detection_1)).
|
||||||
|
{: #port }
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
||||||
|
|
|
@ -67,7 +67,7 @@ For example, to change the routing rule, you could add the label ```"traefik.htt
|
||||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"traefik.http.routers.myrouter.entrypoints": "web,websecure"
|
"traefik.http.routers.myrouter.entrypoints": "ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||||
|
|
|
@ -72,7 +72,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||||
|
|
|
@ -78,8 +78,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.mysql.address=":3306"
|
--entryPoints.mysql.address=:3306
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuring HTTP Routers
|
## Configuring HTTP Routers
|
||||||
|
@ -140,9 +140,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Listens to Specific EntryPoints"
|
??? example "Listens to Specific EntryPoints"
|
||||||
|
@ -198,9 +198,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rule
|
### Rule
|
||||||
|
@ -700,9 +700,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Listens to Specific Entry Points"
|
??? example "Listens to Specific Entry Points"
|
||||||
|
@ -764,9 +764,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rule
|
### Rule
|
||||||
|
|
|
@ -32,7 +32,7 @@ api: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="CLI"
|
```yaml tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--providers.file.filename=dynamic_conf.toml
|
||||||
--api.insecure=true
|
--api.insecure=true
|
||||||
```
|
```
|
||||||
|
@ -153,7 +153,7 @@ api: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="CLI"
|
```yaml tab="CLI"
|
||||||
--entryPoints.websecure.address=":4443"
|
--entryPoints.websecure.address=:4443
|
||||||
# For secure connection on backend.local
|
# For secure connection on backend.local
|
||||||
--serversTransport.rootCAs=./backend.cert
|
--serversTransport.rootCAs=./backend.cert
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--providers.file.filename=dynamic_conf.toml
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.6
|
3.7
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -67,7 +67,7 @@ require (
|
||||||
github.com/opencontainers/runc v1.0.0-rc8 // indirect
|
github.com/opencontainers/runc v1.0.0-rc8 // indirect
|
||||||
github.com/opentracing/basictracer-go v1.0.0 // indirect
|
github.com/opentracing/basictracer-go v1.0.0 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.1.0
|
github.com/opentracing/opentracing-go v1.1.0
|
||||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4
|
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
|
||||||
github.com/openzipkin/zipkin-go v0.2.1
|
github.com/openzipkin/zipkin-go v0.2.1
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/philhofer/fwd v1.0.0 // indirect
|
github.com/philhofer/fwd v1.0.0 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -472,8 +472,8 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI=
|
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
|
||||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
||||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
|
|
|
@ -16,7 +16,9 @@ import (
|
||||||
type ConsulCatalogSuite struct {
|
type ConsulCatalogSuite struct {
|
||||||
BaseSuite
|
BaseSuite
|
||||||
consulClient *api.Client
|
consulClient *api.Client
|
||||||
|
consulAgentClient *api.Client
|
||||||
consulAddress string
|
consulAddress string
|
||||||
|
consulAgentAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
||||||
|
@ -32,6 +34,13 @@ func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
||||||
// Wait for consul to elect itself leader
|
// Wait for consul to elect itself leader
|
||||||
err = s.waitToElectConsulLeader()
|
err = s.waitToElectConsulLeader()
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
s.consulAgentAddress = "http://" + s.composeProject.Container(c, "consul-agent").NetworkSettings.IPAddress + ":8500"
|
||||||
|
clientAgent, err := api.NewClient(&api.Config{
|
||||||
|
Address: s.consulAgentAddress,
|
||||||
|
})
|
||||||
|
c.Check(err, check.IsNil)
|
||||||
|
s.consulAgentClient = clientAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
|
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
|
||||||
|
@ -53,13 +62,17 @@ func (s *ConsulCatalogSuite) TearDownSuite(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) registerService(id, name, address, port string, tags []string) error {
|
func (s *ConsulCatalogSuite) registerService(id, name, address, port string, tags []string, onAgent bool) error {
|
||||||
iPort, err := strconv.Atoi(port)
|
iPort, err := strconv.Atoi(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
client := s.consulClient
|
||||||
|
if onAgent {
|
||||||
|
client = s.consulAgentClient
|
||||||
|
}
|
||||||
|
|
||||||
return s.consulClient.Agent().ServiceRegister(&api.AgentServiceRegistration{
|
return client.Agent().ServiceRegister(&api.AgentServiceRegistration{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: name,
|
Name: name,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
@ -68,16 +81,20 @@ func (s *ConsulCatalogSuite) registerService(id, name, address, port string, tag
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) deregisterService(id string) error {
|
func (s *ConsulCatalogSuite) deregisterService(id string, onAgent bool) error {
|
||||||
return s.consulClient.Agent().ServiceDeregister(id)
|
client := s.consulClient
|
||||||
|
if onAgent {
|
||||||
|
client = s.consulAgentClient
|
||||||
|
}
|
||||||
|
return client.Agent().ServiceDeregister(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *check.C) {
|
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *check.C) {
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"})
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"})
|
err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = s.registerService("whoami3", "whoami", s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"})
|
err = s.registerService("whoami3", "whoami", s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
|
@ -102,11 +119,11 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = s.deregisterService("whoami2")
|
err = s.deregisterService("whoami2", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = s.deregisterService("whoami3")
|
err = s.deregisterService("whoami3", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +135,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
|
||||||
"traefik.http.services.service1.loadBalancer.server.url=http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
|
"traefik.http.services.service1.loadBalancer.server.url=http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels)
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
|
@ -139,7 +156,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +172,7 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"})
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
@ -171,7 +188,7 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +204,7 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
err := s.registerService("whoami1", "whoami", "", "80", []string{"traefik.enable=true"})
|
err := s.registerService("whoami1", "whoami", "", "80", []string{"traefik.enable=true"}, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
@ -202,7 +219,7 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("whoami@consulcatalog", "\"http://127.0.0.1:80\": \"UP\""))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("whoami@consulcatalog", "\"http://127.0.0.1:80\": \"UP\""))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +236,7 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", nil)
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", nil, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
|
@ -236,7 +253,7 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +276,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
|
||||||
"traefik.tcp.Services.Super.Loadbalancer.server.port=8080",
|
"traefik.tcp.Services.Super.Loadbalancer.server.port=8080",
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService("whoamitcp", "whoamitcp", s.composeProject.Container(c, "whoamitcp").NetworkSettings.IPAddress, "8080", labels)
|
err := s.registerService("whoamitcp", "whoamitcp", s.composeProject.Container(c, "whoamitcp").NetworkSettings.IPAddress, "8080", labels, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
|
@ -277,7 +294,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
|
||||||
|
|
||||||
c.Assert(who, checker.Contains, "whoamitcp")
|
c.Assert(who, checker.Contains, "whoamitcp")
|
||||||
|
|
||||||
err = s.deregisterService("whoamitcp")
|
err = s.deregisterService("whoamitcp", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,14 +314,14 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
|
||||||
labels := []string{
|
labels := []string{
|
||||||
"traefik.http.Routers.Super.Rule=Host(`my.super.host`)",
|
"traefik.http.Routers.Super.Rule=Host(`my.super.host`)",
|
||||||
}
|
}
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels)
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Start another container by replacing a '.' by a '-'
|
// Start another container by replacing a '.' by a '-'
|
||||||
labels = []string{
|
labels = []string{
|
||||||
"traefik.http.Routers.SuperHost.Rule=Host(`my-super.host`)",
|
"traefik.http.Routers.SuperHost.Rule=Host(`my-super.host`)",
|
||||||
}
|
}
|
||||||
err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", labels)
|
err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", labels, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
|
@ -328,10 +345,63 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1")
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = s.deregisterService("whoami2")
|
err = s.deregisterService("whoami2", false)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C) {
|
||||||
|
tempObjects := struct {
|
||||||
|
ConsulAddress string
|
||||||
|
DefaultRule string
|
||||||
|
}{
|
||||||
|
ConsulAddress: s.consulAddress,
|
||||||
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
|
}
|
||||||
|
|
||||||
|
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
||||||
|
defer os.Remove(file)
|
||||||
|
|
||||||
|
// Start a container with some labels
|
||||||
|
labels := []string{
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.Routers.Super.service=whoami",
|
||||||
|
"traefik.http.Routers.Super.Rule=Host(`my.super.host`)",
|
||||||
|
}
|
||||||
|
err := s.registerService("whoami", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels, false)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = s.registerService("whoami", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", labels, true)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Start traefik
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
err = cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200),
|
||||||
|
try.BodyContainsOr(s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
|
||||||
|
s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = s.deregisterService("whoami1", false)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = s.deregisterService("whoami2", true)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +421,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
|
||||||
labels := []string{
|
labels := []string{
|
||||||
"traefik.random.value=my.super.host",
|
"traefik.random.value=my.super.host",
|
||||||
}
|
}
|
||||||
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels)
|
err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels, false)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web1]
|
||||||
|
address = ":8000"
|
||||||
|
[entryPoints.web2]
|
||||||
|
address = ":9000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router1]
|
||||||
|
entryPoints = ["web1"]
|
||||||
|
service = "service1"
|
||||||
|
rule = "Host(`test.localhost`)"
|
||||||
|
|
||||||
|
[http.routers.router2]
|
||||||
|
entryPoints = ["web2"]
|
||||||
|
service = "service1"
|
||||||
|
rule = "Host(`test.localhost`)"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.service1.loadBalancer]
|
||||||
|
[http.services.service1.loadBalancer.healthcheck]
|
||||||
|
path = "/health"
|
||||||
|
interval = "1s"
|
||||||
|
timeout = "0.9s"
|
||||||
|
[[http.services.service1.loadBalancer.servers]]
|
||||||
|
url = "http://{{.Server1}}:80"
|
|
@ -205,3 +205,69 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
|
||||||
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if all the loadbalancers created will correctly update the server status
|
||||||
|
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService(c *check.C) {
|
||||||
|
file := s.adaptFile(c, "fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
|
||||||
|
Server1 string
|
||||||
|
}{s.whoami1IP})
|
||||||
|
defer os.Remove(file)
|
||||||
|
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
// wait for traefik
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Set whoami health to 200 to be sure to start with the wanted status
|
||||||
|
client := &http.Client{}
|
||||||
|
statusOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
_, err = client.Do(statusOkReq)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// check healthcheck on web1 entrypoint
|
||||||
|
healthReqWeb1, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
healthReqWeb1.Host = "test.localhost"
|
||||||
|
err = try.Request(healthReqWeb1, 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// check healthcheck on web2 entrypoint
|
||||||
|
healthReqWeb2, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
healthReqWeb2.Host = "test.localhost"
|
||||||
|
|
||||||
|
err = try.Request(healthReqWeb2, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Set whoami health to 500
|
||||||
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("500")))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Verify no backend service is available due to failing health checks
|
||||||
|
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Change one whoami health to 200
|
||||||
|
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
_, err = client.Do(statusOKReq1)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
// Verify health check
|
||||||
|
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
consul:
|
consul:
|
||||||
image: consul:1.6.1
|
image: consul:1.6.2
|
||||||
ports:
|
ports:
|
||||||
- 8500:8500
|
- 8500:8500
|
||||||
|
command: "agent -server -bootstrap -ui -client 0.0.0.0"
|
||||||
|
consul-agent:
|
||||||
|
image: consul:1.6.2
|
||||||
|
ports:
|
||||||
|
- 8501:8500
|
||||||
|
command: "agent -retry-join consul -client 0.0.0.0"
|
||||||
|
links:
|
||||||
|
- consul
|
||||||
whoami1:
|
whoami1:
|
||||||
image: containous/whoami:v1.3.0
|
image: containous/whoami:v1.3.0
|
||||||
hostname: whoami1
|
hostname: whoami1
|
||||||
|
|
|
@ -4,6 +4,7 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
@ -15,6 +16,7 @@ type Command struct {
|
||||||
Configuration interface{}
|
Configuration interface{}
|
||||||
Resources []ResourceLoader
|
Resources []ResourceLoader
|
||||||
Run func([]string) error
|
Run func([]string) error
|
||||||
|
CustomHelpFunc func(io.Writer, *Command) error
|
||||||
Hidden bool
|
Hidden bool
|
||||||
// AllowArg if not set, disallows any argument that is not a known command or a sub-command.
|
// AllowArg if not set, disallows any argument that is not a known command or a sub-command.
|
||||||
AllowArg bool
|
AllowArg bool
|
||||||
|
@ -35,6 +37,15 @@ func (c *Command) AddCommand(cmd *Command) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrintHelp calls the custom help function of the command if it's set.
|
||||||
|
// Otherwise, it calls the default help function.
|
||||||
|
func (c *Command) PrintHelp(w io.Writer) error {
|
||||||
|
if c.CustomHelpFunc != nil {
|
||||||
|
return c.CustomHelpFunc(w, c)
|
||||||
|
}
|
||||||
|
return PrintHelp(w, c)
|
||||||
|
}
|
||||||
|
|
||||||
// Execute Executes a command.
|
// Execute Executes a command.
|
||||||
func Execute(cmd *Command) error {
|
func Execute(cmd *Command) error {
|
||||||
return execute(cmd, os.Args, true)
|
return execute(cmd, os.Args, true)
|
||||||
|
@ -61,11 +72,13 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
|
|
||||||
// Calls command by its name.
|
// Calls command by its name.
|
||||||
if len(args) >= 2 && cmd.Name == args[1] {
|
if len(args) >= 2 && cmd.Name == args[1] {
|
||||||
|
if len(args) < 3 || !contains(cmd.subCommands, args[2]) {
|
||||||
if err := run(cmd, args[2:]); err != nil {
|
if err := run(cmd, args[2:]); err != nil {
|
||||||
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
return fmt.Errorf("command %s error: %v", cmd.Name, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No sub-command, calls the current command.
|
// No sub-command, calls the current command.
|
||||||
if len(cmd.subCommands) == 0 {
|
if len(cmd.subCommands) == 0 {
|
||||||
|
@ -78,6 +91,9 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
// Trying to find the sub-command.
|
// Trying to find the sub-command.
|
||||||
for _, subCmd := range cmd.subCommands {
|
for _, subCmd := range cmd.subCommands {
|
||||||
if len(args) >= 2 && subCmd.Name == args[1] {
|
if len(args) >= 2 && subCmd.Name == args[1] {
|
||||||
|
return execute(subCmd, args, false)
|
||||||
|
}
|
||||||
|
if len(args) >= 3 && subCmd.Name == args[2] {
|
||||||
return execute(subCmd, args[1:], false)
|
return execute(subCmd, args[1:], false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,16 +103,16 @@ func execute(cmd *Command, args []string, root bool) error {
|
||||||
|
|
||||||
func run(cmd *Command, args []string) error {
|
func run(cmd *Command, args []string) error {
|
||||||
if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
|
if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
|
||||||
_ = PrintHelp(os.Stdout, cmd)
|
_ = cmd.PrintHelp(os.Stdout)
|
||||||
return fmt.Errorf("command not found: %s", args[0])
|
return fmt.Errorf("command not found: %s", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if isHelp(args) {
|
if isHelp(args) {
|
||||||
return PrintHelp(os.Stdout, cmd)
|
return cmd.PrintHelp(os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Run == nil {
|
if cmd.Run == nil {
|
||||||
_ = PrintHelp(os.Stdout, cmd)
|
_ = cmd.PrintHelp(os.Stdout)
|
||||||
return fmt.Errorf("command %s is not runnable", cmd.Name)
|
return fmt.Errorf("command %s is not runnable", cmd.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -55,6 +59,63 @@ func TestCommand_AddCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommand_PrintHelp(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
command *Command
|
||||||
|
expectedOutput string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "print default help",
|
||||||
|
command: &Command{},
|
||||||
|
expectedOutput: " \n\nUsage: [command] [flags] [arguments]\n\nUse \" [command] --help\" for help on any command.\n\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "print custom help",
|
||||||
|
command: &Command{
|
||||||
|
Name: "root",
|
||||||
|
Description: "Description for root",
|
||||||
|
Configuration: &struct {
|
||||||
|
Foo []struct {
|
||||||
|
Field string
|
||||||
|
}
|
||||||
|
}{},
|
||||||
|
Run: func(args []string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
CustomHelpFunc: func(w io.Writer, _ *Command) error {
|
||||||
|
_, _ = fmt.Fprintln(w, "test")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOutput: "test\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "error is returned from called help",
|
||||||
|
command: &Command{
|
||||||
|
CustomHelpFunc: func(_ io.Writer, _ *Command) error {
|
||||||
|
return errors.New("test")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: errors.New("test"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
err := test.command.PrintHelp(buffer)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expectedError, err)
|
||||||
|
assert.Equal(t, test.expectedOutput, buffer.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_execute(t *testing.T) {
|
func Test_execute(t *testing.T) {
|
||||||
var called string
|
var called string
|
||||||
|
|
||||||
|
@ -559,6 +620,88 @@ func Test_execute(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: expected{result: "root---foo=bar--fii=bir"},
|
expected: expected{result: "root---foo=bar--fii=bir"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "sub command help",
|
||||||
|
args: []string{"", "test", "subtest", "--help"},
|
||||||
|
command: func() *Command {
|
||||||
|
rootCmd := &Command{
|
||||||
|
Name: "test",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
subCmd := &Command{
|
||||||
|
Name: "subtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rootCmd.AddCommand(subCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubCmd := &Command{
|
||||||
|
Name: "subsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subCmd.AddCommand(subSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubSubCmd := &Command{
|
||||||
|
Name: "subsubsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
Run: func([]string) error {
|
||||||
|
called = "subsubsubtest"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subSubCmd.AddCommand(subSubSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return rootCmd
|
||||||
|
},
|
||||||
|
expected: expected{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "sub sub command help",
|
||||||
|
args: []string{"", "test", "subtest", "subsubtest", "--help"},
|
||||||
|
command: func() *Command {
|
||||||
|
rootCmd := &Command{
|
||||||
|
Name: "test",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
subCmd := &Command{
|
||||||
|
Name: "subtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rootCmd.AddCommand(subCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubCmd := &Command{
|
||||||
|
Name: "subsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subCmd.AddCommand(subSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubSubCmd := &Command{
|
||||||
|
Name: "subsubsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
Run: func([]string) error {
|
||||||
|
called = "subsubsubtest"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subSubCmd.AddCommand(subSubSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return rootCmd
|
||||||
|
},
|
||||||
|
expected: expected{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -756,3 +899,43 @@ Flags:
|
||||||
|
|
||||||
`, string(out))
|
`, string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestName(t *testing.T) {
|
||||||
|
rootCmd := &Command{
|
||||||
|
Name: "test",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
subCmd := &Command{
|
||||||
|
Name: "subtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rootCmd.AddCommand(subCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubCmd := &Command{
|
||||||
|
Name: "subsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
Run: func([]string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subCmd.AddCommand(subSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subSubSubCmd := &Command{
|
||||||
|
Name: "subsubsubtest",
|
||||||
|
Resources: []ResourceLoader{&FlagLoader{}},
|
||||||
|
Run: func([]string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = subSubCmd.AddCommand(subSubSubCmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = execute(rootCmd, []string{"", "test", "subtest", "subsubtest", "subsubsubtest", "--help"}, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ func (f *FileLoader) GetFilename() string {
|
||||||
func (f *FileLoader) Load(args []string, cmd *Command) (bool, error) {
|
func (f *FileLoader) Load(args []string, cmd *Command) (bool, error) {
|
||||||
ref, err := flag.Parse(args, cmd.Configuration)
|
ref, err := flag.Parse(args, cmd.Configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = PrintHelp(os.Stdout, cmd)
|
_ = cmd.PrintHelp(os.Stdout)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,20 @@ const (
|
||||||
var singleton *HealthCheck
|
var singleton *HealthCheck
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
// BalancerHandler includes functionality for load-balancing management.
|
// Balancer is the set of operations required to manage the list of servers in a
|
||||||
type BalancerHandler interface {
|
// load-balancer.
|
||||||
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
type Balancer interface {
|
||||||
Servers() []*url.URL
|
Servers() []*url.URL
|
||||||
RemoveServer(u *url.URL) error
|
RemoveServer(u *url.URL) error
|
||||||
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error
|
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BalancerHandler includes functionality for load-balancing management.
|
||||||
|
type BalancerHandler interface {
|
||||||
|
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||||
|
Balancer
|
||||||
|
}
|
||||||
|
|
||||||
// metricsRegistry is a local interface in the health check package, exposing only the required metrics
|
// metricsRegistry is a local interface in the health check package, exposing only the required metrics
|
||||||
// necessary for the health check package. This makes it easier for the tests.
|
// necessary for the health check package. This makes it easier for the tests.
|
||||||
type metricsRegistry interface {
|
type metricsRegistry interface {
|
||||||
|
@ -49,7 +55,7 @@ type Options struct {
|
||||||
Transport http.RoundTripper
|
Transport http.RoundTripper
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
LB BalancerHandler
|
LB Balancer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt Options) String() string {
|
func (opt Options) String() string {
|
||||||
|
@ -146,18 +152,18 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
||||||
enabledURLs := backend.LB.Servers()
|
enabledURLs := backend.LB.Servers()
|
||||||
var newDisabledURLs []backendURL
|
var newDisabledURLs []backendURL
|
||||||
// FIXME re enable metrics
|
// FIXME re enable metrics
|
||||||
for _, disableURL := range backend.disabledURLs {
|
for _, disabledURL := range backend.disabledURLs {
|
||||||
// FIXME serverUpMetricValue := float64(0)
|
// FIXME serverUpMetricValue := float64(0)
|
||||||
if err := checkHealth(disableURL.url, backend); err == nil {
|
if err := checkHealth(disabledURL.url, backend); err == nil {
|
||||||
logger.Warnf("Health check up: Returning to server list. Backend: %q URL: %q Weight: %d",
|
logger.Warnf("Health check up: Returning to server list. Backend: %q URL: %q Weight: %d",
|
||||||
backend.name, disableURL.url.String(), disableURL.weight)
|
backend.name, disabledURL.url.String(), disabledURL.weight)
|
||||||
if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil {
|
if err = backend.LB.UpsertServer(disabledURL.url, roundrobin.Weight(disabledURL.weight)); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
// FIXME serverUpMetricValue = 1
|
// FIXME serverUpMetricValue = 1
|
||||||
} else {
|
} else {
|
||||||
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disableURL.url.String(), err)
|
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disabledURL.url.String(), err)
|
||||||
newDisabledURLs = append(newDisabledURLs, disableURL)
|
newDisabledURLs = append(newDisabledURLs, disabledURL)
|
||||||
}
|
}
|
||||||
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
|
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
|
||||||
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
|
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
|
||||||
|
@ -177,7 +183,7 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
||||||
weight = 1
|
weight = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Warnf("Health check failed: Remove from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
|
logger.Warnf("Health check failed, removing from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
|
||||||
if err := backend.LB.RemoveServer(enableURL); err != nil {
|
if err := backend.LB.RemoveServer(enableURL); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -281,3 +287,38 @@ func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.Server
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Balancers is a list of Balancers(s) that implements the Balancer interface.
|
||||||
|
type Balancers []Balancer
|
||||||
|
|
||||||
|
// Servers returns the servers url from all the BalancerHandler
|
||||||
|
func (b Balancers) Servers() []*url.URL {
|
||||||
|
var servers []*url.URL
|
||||||
|
for _, lb := range b {
|
||||||
|
servers = append(servers, lb.Servers()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveServer removes the given server from all the BalancerHandler,
|
||||||
|
// and updates the status of the server to "DOWN".
|
||||||
|
func (b Balancers) RemoveServer(u *url.URL) error {
|
||||||
|
for _, lb := range b {
|
||||||
|
if err := lb.RemoveServer(u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpsertServer adds the given server to all the BalancerHandler,
|
||||||
|
// and updates the status of the server to "UP".
|
||||||
|
func (b Balancers) UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error {
|
||||||
|
for _, lb := range b {
|
||||||
|
if err := lb.UpsertServer(u, options...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -116,7 +116,18 @@ type CoreLogData map[string]interface{}
|
||||||
// LogData is the data captured by the middleware so that it can be logged.
|
// LogData is the data captured by the middleware so that it can be logged.
|
||||||
type LogData struct {
|
type LogData struct {
|
||||||
Core CoreLogData
|
Core CoreLogData
|
||||||
Request http.Header
|
Request request
|
||||||
OriginResponse http.Header
|
OriginResponse http.Header
|
||||||
DownstreamResponse http.Header
|
DownstreamResponse downstreamResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type downstreamResponse struct {
|
||||||
|
headers http.Header
|
||||||
|
status int
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
headers http.Header
|
||||||
|
count int64
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,6 @@ func (n noopCloser) Close() error {
|
||||||
|
|
||||||
type handlerParams struct {
|
type handlerParams struct {
|
||||||
logDataTable *LogData
|
logDataTable *LogData
|
||||||
crr *captureRequestReader
|
|
||||||
crw *captureResponseWriter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler will write each request and its response to the access log.
|
// Handler will write each request and its response to the access log.
|
||||||
|
@ -122,7 +120,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
|
||||||
go func() {
|
go func() {
|
||||||
defer logHandler.wg.Done()
|
defer logHandler.wg.Done()
|
||||||
for handlerParams := range logHandler.logHandlerChan {
|
for handlerParams := range logHandler.logHandlerChan {
|
||||||
logHandler.logTheRoundTrip(handlerParams.logDataTable, handlerParams.crr, handlerParams.crw)
|
logHandler.logTheRoundTrip(handlerParams.logDataTable)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -162,7 +160,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
||||||
StartLocal: now.Local(),
|
StartLocal: now.Local(),
|
||||||
}
|
}
|
||||||
|
|
||||||
logDataTable := &LogData{Core: core, Request: req.Header}
|
logDataTable := &LogData{
|
||||||
|
Core: core,
|
||||||
|
Request: request{
|
||||||
|
headers: req.Header,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
|
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
|
||||||
|
|
||||||
|
@ -205,16 +208,21 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
||||||
core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
|
core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
logDataTable.DownstreamResponse = crw.Header()
|
logDataTable.DownstreamResponse = downstreamResponse{
|
||||||
|
headers: crw.Header().Clone(),
|
||||||
|
status: crw.Status(),
|
||||||
|
size: crw.Size(),
|
||||||
|
}
|
||||||
|
if crr != nil {
|
||||||
|
logDataTable.Request.count = crr.count
|
||||||
|
}
|
||||||
|
|
||||||
if h.config.BufferingSize > 0 {
|
if h.config.BufferingSize > 0 {
|
||||||
h.logHandlerChan <- handlerParams{
|
h.logHandlerChan <- handlerParams{
|
||||||
logDataTable: logDataTable,
|
logDataTable: logDataTable,
|
||||||
crr: crr,
|
|
||||||
crw: crw,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
h.logTheRoundTrip(logDataTable, crr, crw)
|
h.logTheRoundTrip(logDataTable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +272,7 @@ func usernameIfPresent(theURL *url.URL) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging handler to log frontend name, backend name, and elapsed time.
|
// Logging handler to log frontend name, backend name, and elapsed time.
|
||||||
func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
|
func (h *Handler) logTheRoundTrip(logDataTable *LogData) {
|
||||||
core := logDataTable.Core
|
core := logDataTable.Core
|
||||||
|
|
||||||
retryAttempts, ok := core[RetryAttempts].(int)
|
retryAttempts, ok := core[RetryAttempts].(int)
|
||||||
|
@ -272,23 +280,22 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
|
||||||
retryAttempts = 0
|
retryAttempts = 0
|
||||||
}
|
}
|
||||||
core[RetryAttempts] = retryAttempts
|
core[RetryAttempts] = retryAttempts
|
||||||
|
core[RequestContentSize] = logDataTable.Request.count
|
||||||
|
|
||||||
if crr != nil {
|
status := logDataTable.DownstreamResponse.status
|
||||||
core[RequestContentSize] = crr.count
|
core[DownstreamStatus] = status
|
||||||
}
|
|
||||||
|
|
||||||
core[DownstreamStatus] = crw.Status()
|
|
||||||
|
|
||||||
// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
|
// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
|
||||||
totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
|
totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
|
||||||
core[Duration] = totalDuration
|
core[Duration] = totalDuration
|
||||||
|
|
||||||
if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
|
if h.keepAccessLog(status, retryAttempts, totalDuration) {
|
||||||
core[DownstreamContentSize] = crw.Size()
|
size := logDataTable.DownstreamResponse.size
|
||||||
|
core[DownstreamContentSize] = size
|
||||||
if original, ok := core[OriginContentSize]; ok {
|
if original, ok := core[OriginContentSize]; ok {
|
||||||
o64 := original.(int64)
|
o64 := original.(int64)
|
||||||
if crw.Size() != o64 && crw.Size() != 0 {
|
if size != o64 && size != 0 {
|
||||||
core[GzipRatio] = float64(o64) / float64(crw.Size())
|
core[GzipRatio] = float64(o64) / float64(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,9 +312,9 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.redactHeaders(logDataTable.Request, fields, "request_")
|
h.redactHeaders(logDataTable.Request.headers, fields, "request_")
|
||||||
h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
|
h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
|
||||||
h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")
|
h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_")
|
||||||
|
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
defer h.mu.Unlock()
|
||||||
|
|
|
@ -192,6 +192,7 @@ func TestLoggerJSON(t *testing.T) {
|
||||||
Format: JSONFormat,
|
Format: JSONFormat,
|
||||||
},
|
},
|
||||||
expected: map[string]func(t *testing.T, value interface{}){
|
expected: map[string]func(t *testing.T, value interface{}){
|
||||||
|
RequestContentSize: assertFloat64(0),
|
||||||
RequestHost: assertString(testHostname),
|
RequestHost: assertString(testHostname),
|
||||||
RequestAddr: assertString(testHostname),
|
RequestAddr: assertString(testHostname),
|
||||||
RequestMethod: assertString(testMethod),
|
RequestMethod: assertString(testMethod),
|
||||||
|
|
|
@ -221,13 +221,11 @@ func (s *Header) processCorsHeaders(rw http.ResponseWriter, req *http.Request) b
|
||||||
}
|
}
|
||||||
|
|
||||||
reqAcMethod := req.Header.Get("Access-Control-Request-Method")
|
reqAcMethod := req.Header.Get("Access-Control-Request-Method")
|
||||||
reqAcHeaders := req.Header.Get("Access-Control-Request-Headers")
|
|
||||||
originHeader := req.Header.Get("Origin")
|
originHeader := req.Header.Get("Origin")
|
||||||
|
|
||||||
if reqAcMethod != "" && reqAcHeaders != "" && originHeader != "" && req.Method == http.MethodOptions {
|
if reqAcMethod != "" && originHeader != "" && req.Method == http.MethodOptions {
|
||||||
// If the request is an OPTIONS request with an Access-Control-Request-Method header,
|
// If the request is an OPTIONS request with an Access-Control-Request-Method header,
|
||||||
// and Access-Control-Request-Headers headers, and Origin headers,
|
// and Origin headers, then it is a CORS preflight request,
|
||||||
// then it is a CORS preflight request,
|
|
||||||
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
|
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
|
||||||
if s.headers.AccessControlAllowCredentials {
|
if s.headers.AccessControlAllowCredentials {
|
||||||
rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
|
|
@ -275,6 +275,25 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
|
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "No Request Headers Preflight",
|
||||||
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
|
AccessControlAllowOrigin: "*",
|
||||||
|
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
||||||
|
AccessControlMaxAge: 600,
|
||||||
|
}),
|
||||||
|
requestHeaders: map[string][]string{
|
||||||
|
"Access-Control-Request-Method": {"GET", "OPTIONS"},
|
||||||
|
"Origin": {"https://foo.bar.org"},
|
||||||
|
},
|
||||||
|
expected: map[string][]string{
|
||||||
|
"Access-Control-Allow-Origin": {"*"},
|
||||||
|
"Access-Control-Max-Age": {"600"},
|
||||||
|
"Access-Control-Allow-Methods": {"GET,OPTIONS,PUT"},
|
||||||
|
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
|
|
@ -34,7 +34,11 @@ type entryPointMiddleware struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
spanCtx, _ := e.Extract(opentracing.HTTPHeaders, tracing.HTTPHeadersCarrier(req.Header))
|
spanCtx, err := e.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
|
||||||
|
if err != nil {
|
||||||
|
log.FromContext(middlewares.GetLoggerCtx(req.Context(), "tracing", entryPointTypeName)).
|
||||||
|
Debugf("Failed to extract the context: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
|
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
|
||||||
defer finish()
|
defer finish()
|
||||||
|
|
|
@ -12,23 +12,23 @@ import (
|
||||||
// It is used in order to create a specific and unique pattern for these labels.
|
// It is used in order to create a specific and unique pattern for these labels.
|
||||||
const MarathonConstraintPrefix = "Traefik-Marathon-505F9E15-BDC7-45E7-828D-C06C7BAB8091"
|
const MarathonConstraintPrefix = "Traefik-Marathon-505F9E15-BDC7-45E7-828D-C06C7BAB8091"
|
||||||
|
|
||||||
type constraintFunc func(map[string]string) bool
|
type constraintLabelFunc func(map[string]string) bool
|
||||||
|
|
||||||
// Match reports whether the expression matches with the given labels.
|
// MatchLabels reports whether the expression matches with the given labels.
|
||||||
// The expression must match any logical boolean combination of:
|
// The expression must match any logical boolean combination of:
|
||||||
// - `Label(labelName, labelValue)`
|
// - `Label(labelName, labelValue)`
|
||||||
// - `LabelRegex(labelName, regexValue)`
|
// - `LabelRegex(labelName, regexValue)`
|
||||||
// - `MarathonConstraint(field:operator:value)`
|
// - `MarathonConstraint(field:operator:value)`
|
||||||
func Match(labels map[string]string, expr string) (bool, error) {
|
func MatchLabels(labels map[string]string, expr string) (bool, error) {
|
||||||
if expr == "" {
|
if expr == "" {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := predicate.NewParser(predicate.Def{
|
p, err := predicate.NewParser(predicate.Def{
|
||||||
Operators: predicate.Operators{
|
Operators: predicate.Operators{
|
||||||
AND: andFunc,
|
AND: andLabelFunc,
|
||||||
NOT: notFunc,
|
NOT: notLabelFunc,
|
||||||
OR: orFunc,
|
OR: orLabelFunc,
|
||||||
},
|
},
|
||||||
Functions: map[string]interface{}{
|
Functions: map[string]interface{}{
|
||||||
"Label": labelFn,
|
"Label": labelFn,
|
||||||
|
@ -45,20 +45,20 @@ func Match(labels map[string]string, expr string) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fn, ok := parse.(constraintFunc)
|
fn, ok := parse.(constraintLabelFunc)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, errors.New("not a constraintFunc")
|
return false, errors.New("not a constraintLabelFunc")
|
||||||
}
|
}
|
||||||
return fn(labels), nil
|
return fn(labels), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func labelFn(name, value string) constraintFunc {
|
func labelFn(name, value string) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
return labels[name] == value
|
return labels[name] == value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func labelRegexFn(name, expr string) constraintFunc {
|
func labelRegexFn(name, expr string) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
matched, err := regexp.MatchString(expr, labels[name])
|
matched, err := regexp.MatchString(expr, labels[name])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -68,7 +68,7 @@ func labelRegexFn(name, expr string) constraintFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func marathonFn(value string) constraintFunc {
|
func marathonFn(value string) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
for k, v := range labels {
|
for k, v := range labels {
|
||||||
if strings.HasPrefix(k, MarathonConstraintPrefix) {
|
if strings.HasPrefix(k, MarathonConstraintPrefix) {
|
||||||
|
@ -81,19 +81,19 @@ func marathonFn(value string) constraintFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func andFunc(a, b constraintFunc) constraintFunc {
|
func andLabelFunc(a, b constraintLabelFunc) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
return a(labels) && b(labels)
|
return a(labels) && b(labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func orFunc(a, b constraintFunc) constraintFunc {
|
func orLabelFunc(a, b constraintLabelFunc) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
return a(labels) || b(labels)
|
return a(labels) || b(labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func notFunc(a constraintFunc) constraintFunc {
|
func notLabelFunc(a constraintLabelFunc) constraintLabelFunc {
|
||||||
return func(labels map[string]string) bool {
|
return func(labels map[string]string) bool {
|
||||||
return !a(labels)
|
return !a(labels)
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMatch(t *testing.T) {
|
func TestMatchLabels(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
expr string
|
expr string
|
||||||
labels map[string]string
|
labels map[string]string
|
||||||
|
@ -192,7 +192,7 @@ func TestMatch(t *testing.T) {
|
||||||
t.Run(test.expr, func(t *testing.T) {
|
t.Run(test.expr, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
matches, err := Match(test.labels, test.expr)
|
matches, err := MatchLabels(test.labels, test.expr)
|
||||||
if test.expectedErr {
|
if test.expectedErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
} else {
|
} else {
|
92
pkg/provider/constraints/constraints_tags.go
Normal file
92
pkg/provider/constraints/constraints_tags.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package constraints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/vulcand/predicate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type constraintTagFunc func([]string) bool
|
||||||
|
|
||||||
|
// MatchTags reports whether the expression matches with the given tags.
|
||||||
|
// The expression must match any logical boolean combination of:
|
||||||
|
// - `Tag(tagValue)`
|
||||||
|
// - `TagRegex(regexValue)`
|
||||||
|
func MatchTags(tags []string, expr string) (bool, error) {
|
||||||
|
if expr == "" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := predicate.NewParser(predicate.Def{
|
||||||
|
Operators: predicate.Operators{
|
||||||
|
AND: andTagFunc,
|
||||||
|
NOT: notTagFunc,
|
||||||
|
OR: orTagFunc,
|
||||||
|
},
|
||||||
|
Functions: map[string]interface{}{
|
||||||
|
"Tag": tagFn,
|
||||||
|
"TagRegex": tagRegexFn,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parse, err := p.Parse(expr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fn, ok := parse.(constraintTagFunc)
|
||||||
|
if !ok {
|
||||||
|
return false, errors.New("not a constraintTagFunc")
|
||||||
|
}
|
||||||
|
return fn(tags), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagFn(name string) constraintTagFunc {
|
||||||
|
return func(tags []string) bool {
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagRegexFn(expr string) constraintTagFunc {
|
||||||
|
return func(tags []string) bool {
|
||||||
|
exp, err := regexp.Compile(expr)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if exp.MatchString(tag) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func andTagFunc(a, b constraintTagFunc) constraintTagFunc {
|
||||||
|
return func(tags []string) bool {
|
||||||
|
return a(tags) && b(tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func orTagFunc(a, b constraintTagFunc) constraintTagFunc {
|
||||||
|
return func(tags []string) bool {
|
||||||
|
return a(tags) || b(tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func notTagFunc(a constraintTagFunc) constraintTagFunc {
|
||||||
|
return func(tags []string) bool {
|
||||||
|
return !a(tags)
|
||||||
|
}
|
||||||
|
}
|
111
pkg/provider/constraints/constraints_tags_test.go
Normal file
111
pkg/provider/constraints/constraints_tags_test.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package constraints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatchTags(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
expr string
|
||||||
|
tags []string
|
||||||
|
expected bool
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
expr: `Tag("world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("worlds")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `!Tag("world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello") && Tag("world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello") && Tag("worlds")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello") && !Tag("world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello") || Tag( "world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag( "worlds") || Tag("hello")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello") || !Tag("world")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag()`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Foo("hello")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `Tag("hello")`,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: ``,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `TagRegex("hel\\w+")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `TagRegex("hell\\w+s")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `!TagRegex("hel\\w+")`,
|
||||||
|
tags: []string{"hello", "world"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.expr, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
matches, err := MatchTags(test.tags, test.expr)
|
||||||
|
if test.expectedErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.expected, matches)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData) *dy
|
||||||
configurations := make(map[string]*dynamic.Configuration)
|
configurations := make(map[string]*dynamic.Configuration)
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
svcName := item.Name + "-" + item.ID
|
svcName := item.Node + "-" + item.Name + "-" + item.ID
|
||||||
ctxSvc := log.With(ctx, log.Str("serviceName", svcName))
|
ctxSvc := log.With(ctx, log.Str("serviceName", svcName))
|
||||||
|
|
||||||
if !p.keepContainer(ctxSvc, item) {
|
if !p.keepContainer(ctxSvc, item) {
|
||||||
|
@ -80,7 +80,7 @@ func (p *Provider) keepContainer(ctx context.Context, item itemData) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
matches, err := constraints.Match(item.Labels, p.Constraints)
|
matches, err := constraints.MatchTags(item.Tags, p.Constraints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error matching constraints expression: %v", err)
|
logger.Errorf("Error matching constraints expression: %v", err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -2,6 +2,7 @@ package consulcatalog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||||
|
@ -25,6 +26,7 @@ func TestDefaultRule(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "id",
|
ID: "id",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
Port: "80",
|
Port: "80",
|
||||||
|
@ -66,6 +68,7 @@ func TestDefaultRule(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "id",
|
ID: "id",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
Port: "80",
|
Port: "80",
|
||||||
|
@ -109,6 +112,7 @@ func TestDefaultRule(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -145,6 +149,7 @@ func TestDefaultRule(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -181,6 +186,7 @@ func TestDefaultRule(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -257,6 +263,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -297,6 +304,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -305,6 +313,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "Test2",
|
ID: "Test2",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test2",
|
Name: "Test2",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.2",
|
Address: "127.0.0.2",
|
||||||
|
@ -359,6 +368,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "1",
|
ID: "1",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -367,6 +377,110 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "2",
|
ID: "2",
|
||||||
|
Node: "Node1",
|
||||||
|
Name: "Test",
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Address: "127.0.0.2",
|
||||||
|
Port: "80",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"Test": {
|
||||||
|
Service: "Test",
|
||||||
|
Rule: "Host(`Test.traefik.wtf`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"Test": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://127.0.0.1:80",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://127.0.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "two containers with same service name & id no label on same node",
|
||||||
|
items: []itemData{
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Node: "Node1",
|
||||||
|
Name: "Test",
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Port: "80",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Node: "Node1",
|
||||||
|
Name: "Test",
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Address: "127.0.0.2",
|
||||||
|
Port: "80",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"Test": {
|
||||||
|
Service: "Test",
|
||||||
|
Rule: "Host(`Test.traefik.wtf`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"Test": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://127.0.0.2:80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "two containers with same service name & id no label on different nodes",
|
||||||
|
items: []itemData{
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Node: "Node1",
|
||||||
|
Name: "Test",
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Port: "80",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Node: "Node2",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.2",
|
Address: "127.0.0.2",
|
||||||
|
@ -1320,6 +1434,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.2",
|
Address: "127.0.0.2",
|
||||||
|
@ -1393,6 +1508,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
items: []itemData{
|
items: []itemData{
|
||||||
{
|
{
|
||||||
ID: "Test",
|
ID: "Test",
|
||||||
|
Node: "Node1",
|
||||||
Name: "Test",
|
Name: "Test",
|
||||||
Labels: map[string]string{},
|
Labels: map[string]string{},
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
|
@ -1426,7 +1542,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Status: api.HealthPassing,
|
Status: api.HealthPassing,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: `Label("traefik.tags", "bar")`,
|
constraints: `Tag("traefik.tags=bar")`,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
@ -1453,7 +1569,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
Status: api.HealthPassing,
|
Status: api.HealthPassing,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constraints: `Label("traefik.tags", "foo")`,
|
constraints: `Tag("traefik.tags=foo")`,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
|
@ -1840,6 +1956,12 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
test.items[i].ExtraConf, err = p.getConfiguration(test.items[i])
|
test.items[i].ExtraConf, err = p.getConfiguration(test.items[i])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var tags []string
|
||||||
|
for k, v := range test.items[i].Labels {
|
||||||
|
tags = append(tags, fmt.Sprintf("%s=%s", k, v))
|
||||||
|
}
|
||||||
|
test.items[i].Tags = tags
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := p.buildConfiguration(context.Background(), test.items)
|
configuration := p.buildConfiguration(context.Background(), test.items)
|
||||||
|
|
|
@ -24,11 +24,13 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
type itemData struct {
|
type itemData struct {
|
||||||
ID string
|
ID string
|
||||||
|
Node string
|
||||||
Name string
|
Name string
|
||||||
Address string
|
Address string
|
||||||
Port string
|
Port string
|
||||||
Status string
|
Status string
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
|
Tags []string
|
||||||
ExtraConf configuration
|
ExtraConf configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +158,6 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, consulService := range consulServices {
|
for _, consulService := range consulServices {
|
||||||
labels := tagsToNeutralLabels(consulService.ServiceTags, p.Prefix)
|
|
||||||
address := consulService.ServiceAddress
|
address := consulService.ServiceAddress
|
||||||
if address == "" {
|
if address == "" {
|
||||||
address = consulService.Address
|
address = consulService.Address
|
||||||
|
@ -164,10 +165,12 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
|
||||||
|
|
||||||
item := itemData{
|
item := itemData{
|
||||||
ID: consulService.ServiceID,
|
ID: consulService.ServiceID,
|
||||||
|
Node: consulService.Node,
|
||||||
Name: consulService.ServiceName,
|
Name: consulService.ServiceName,
|
||||||
Address: address,
|
Address: address,
|
||||||
Port: strconv.Itoa(consulService.ServicePort),
|
Port: strconv.Itoa(consulService.ServicePort),
|
||||||
Labels: labels,
|
Labels: tagsToNeutralLabels(consulService.ServiceTags, p.Prefix),
|
||||||
|
Tags: consulService.ServiceTags,
|
||||||
Status: consulService.Checks.AggregatedStatus(),
|
Status: consulService.Checks.AggregatedStatus(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTagsToNeutralLabels(t *testing.T) {
|
func Test_tagsToNeutralLabels(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
tags []string
|
tags []string
|
||||||
|
|
|
@ -127,7 +127,7 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
matches, err := constraints.Match(container.Labels, p.Constraints)
|
matches, err := constraints.MatchLabels(container.Labels, p.Constraints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error matching constraints expression: %v", err)
|
logger.Errorf("Error matching constraints expression: %v", err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -185,7 +185,7 @@ func (p *Provider) keepApplication(ctx context.Context, extraConf configuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by constraints.
|
// Filter by constraints.
|
||||||
matches, err := constraints.Match(labels, p.Constraints)
|
matches, err := constraints.MatchLabels(labels, p.Constraints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error matching constraints expression: %v", err)
|
logger.Errorf("Error matching constraints expression: %v", err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
matches, err := constraints.Match(service.Labels, p.Constraints)
|
matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error matching constraints expression: %v", err)
|
logger.Errorf("Error matching constraints expression: %v", err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -193,7 +193,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
|
||||||
}
|
}
|
||||||
|
|
||||||
service := rancherData{
|
service := rancherData{
|
||||||
Name: service.Name + "/" + stack.Name,
|
Name: service.Name + "_" + stack.Name,
|
||||||
State: service.State,
|
State: service.State,
|
||||||
Labels: service.Labels,
|
Labels: service.Labels,
|
||||||
Port: servicePort,
|
Port: servicePort,
|
||||||
|
|
|
@ -40,7 +40,7 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt
|
||||||
metricsRegistry: metricsRegistry,
|
metricsRegistry: metricsRegistry,
|
||||||
bufferPool: newBufferPool(),
|
bufferPool: newBufferPool(),
|
||||||
defaultRoundTripper: defaultRoundTripper,
|
defaultRoundTripper: defaultRoundTripper,
|
||||||
balancers: make(map[string][]healthcheck.BalancerHandler),
|
balancers: make(map[string]healthcheck.Balancers),
|
||||||
configs: configs,
|
configs: configs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,11 @@ type Manager struct {
|
||||||
metricsRegistry metrics.Registry
|
metricsRegistry metrics.Registry
|
||||||
bufferPool httputil.BufferPool
|
bufferPool httputil.BufferPool
|
||||||
defaultRoundTripper http.RoundTripper
|
defaultRoundTripper http.RoundTripper
|
||||||
balancers map[string][]healthcheck.BalancerHandler
|
// balancers is the map of all Balancers, keyed by service name.
|
||||||
|
// There is one Balancer per service handler, and there is one service handler per reference to a service
|
||||||
|
// (e.g. if 2 routers refer to the same service name, 2 service handlers are created),
|
||||||
|
// which is why there is not just one Balancer per service name.
|
||||||
|
balancers map[string]healthcheck.Balancers
|
||||||
configs map[string]*runtime.ServiceInfo
|
configs map[string]*runtime.ServiceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,14 +96,14 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
||||||
}
|
}
|
||||||
case conf.Weighted != nil:
|
case conf.Weighted != nil:
|
||||||
var err error
|
var err error
|
||||||
lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.AddError(err, true)
|
conf.AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case conf.Mirroring != nil:
|
case conf.Mirroring != nil:
|
||||||
var err error
|
var err error
|
||||||
lb, err = m.getLoadBalancerMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier)
|
lb, err = m.getMirrorServiceHandler(ctx, conf.Mirroring, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.AddError(err, true)
|
conf.AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -113,7 +117,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
||||||
return lb, nil
|
return lb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, serviceName string, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
|
func (m *Manager) getMirrorServiceHandler(ctx context.Context, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||||
serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier)
|
serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -134,7 +138,7 @@ func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, servi
|
||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) getLoadBalancerWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
|
func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||||
// TODO Handle accesslog and metrics with multiple service name
|
// TODO Handle accesslog and metrics with multiple service name
|
||||||
if config.Sticky != nil && config.Sticky.Cookie != nil {
|
if config.Sticky != nil && config.Sticky.Cookie != nil {
|
||||||
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
|
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
|
||||||
|
@ -200,15 +204,12 @@ func (m *Manager) LaunchHealthCheck() {
|
||||||
for serviceName, balancers := range m.balancers {
|
for serviceName, balancers := range m.balancers {
|
||||||
ctx := log.With(context.Background(), log.Str(log.ServiceName, serviceName))
|
ctx := log.With(context.Background(), log.Str(log.ServiceName, serviceName))
|
||||||
|
|
||||||
// TODO aggregate
|
|
||||||
balancer := balancers[0]
|
|
||||||
|
|
||||||
// TODO Should all the services handle healthcheck? Handle different types
|
// TODO Should all the services handle healthcheck? Handle different types
|
||||||
service := m.configs[serviceName].LoadBalancer
|
service := m.configs[serviceName].LoadBalancer
|
||||||
|
|
||||||
// Health Check
|
// Health Check
|
||||||
var backendHealthCheck *healthcheck.BackendConfig
|
var backendHealthCheck *healthcheck.BackendConfig
|
||||||
if hcOpts := buildHealthCheckOptions(ctx, balancer, serviceName, service.HealthCheck); hcOpts != nil {
|
if hcOpts := buildHealthCheckOptions(ctx, balancers, serviceName, service.HealthCheck); hcOpts != nil {
|
||||||
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
|
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
|
||||||
|
|
||||||
hcOpts.Transport = m.defaultRoundTripper
|
hcOpts.Transport = m.defaultRoundTripper
|
||||||
|
@ -224,7 +225,7 @@ func (m *Manager) LaunchHealthCheck() {
|
||||||
healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs)
|
healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
|
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.Balancer, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
|
||||||
if hc == nil || hc.Path == "" {
|
if hc == nil || hc.Path == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// HTTPHeadersCarrier custom implementation to fix duplicated headers
|
|
||||||
// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191
|
|
||||||
type HTTPHeadersCarrier http.Header
|
|
||||||
|
|
||||||
// Set conforms to the TextMapWriter interface.
|
|
||||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
|
||||||
h := http.Header(c)
|
|
||||||
h.Set(key, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForeachKey conforms to the TextMapReader interface.
|
|
||||||
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
|
|
||||||
for k, vals := range c {
|
|
||||||
for _, v := range vals {
|
|
||||||
if err := handler(k, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -134,7 +134,7 @@ func InjectRequestHeaders(r *http.Request) {
|
||||||
err := opentracing.GlobalTracer().Inject(
|
err := opentracing.GlobalTracer().Inject(
|
||||||
span.Context(),
|
span.Context(),
|
||||||
opentracing.HTTPHeaders,
|
opentracing.HTTPHeaders,
|
||||||
HTTPHeadersCarrier(r.Header))
|
opentracing.HTTPHeadersCarrier(r.Header))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(r.Context()).Error(err)
|
log.FromContext(r.Context()).Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,13 @@ import { APP } from '../_helpers/APP'
|
||||||
const apiBase = '/http'
|
const apiBase = '/http'
|
||||||
|
|
||||||
function getAllRouters (params) {
|
function getAllRouters (params) {
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
|
||||||
.then(body => {
|
|
||||||
const total = body.data ? body.data.length : 0
|
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouterByName (name) {
|
function getRouterByName (name) {
|
||||||
|
@ -24,16 +21,13 @@ function getRouterByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllServices (params) {
|
function getAllServices (params) {
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
|
||||||
.then(body => {
|
|
||||||
const total = body.data ? body.data.length : 0
|
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceByName (name) {
|
function getServiceByName (name) {
|
||||||
|
@ -45,16 +39,13 @@ function getServiceByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllMiddlewares (params) {
|
function getAllMiddlewares (params) {
|
||||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`)
|
|
||||||
.then(body => {
|
|
||||||
const total = body.data ? body.data.length : 0
|
|
||||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMiddlewareByName (name) {
|
function getMiddlewareByName (name) {
|
||||||
|
|
|
@ -3,16 +3,13 @@ import { APP } from '../_helpers/APP'
|
||||||
const apiBase = '/tcp'
|
const apiBase = '/tcp'
|
||||||
|
|
||||||
function getAllRouters (params) {
|
function getAllRouters (params) {
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
|
||||||
.then(body => {
|
|
||||||
const total = body.data ? body.data.length : 0
|
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouterByName (name) {
|
function getRouterByName (name) {
|
||||||
|
@ -24,16 +21,13 @@ function getRouterByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllServices (params) {
|
function getAllServices (params) {
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
|
||||||
.then(body => {
|
|
||||||
const total = body.data ? body.data.length : 0
|
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceByName (name) {
|
function getServiceByName (name) {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="text-subtitle2">RULE</div>
|
<div class="text-subtitle2">RULE</div>
|
||||||
<q-chip
|
<q-chip
|
||||||
dense
|
dense
|
||||||
class="app-chip app-chip-rule">
|
class="app-chip app-chip-wrap app-chip-rule">
|
||||||
{{ data.rule }}
|
{{ data.rule }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<div class="text-subtitle2">NAME</div>
|
<div class="text-subtitle2">NAME</div>
|
||||||
<q-chip
|
<q-chip
|
||||||
dense
|
dense
|
||||||
class="app-chip app-chip-name">
|
class="app-chip app-chip-wrap app-chip-name">
|
||||||
{{ data.name }}
|
{{ data.name }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
dense
|
dense
|
||||||
clickable
|
clickable
|
||||||
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
|
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
|
||||||
class="app-chip app-chip-service">
|
class="app-chip app-chip-wrap app-chip-service">
|
||||||
{{ data.service }}
|
{{ data.service }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section v-if="data.loadBalancer">
|
<q-card-section v-if="data.loadBalancer && $route.meta.protocol !== 'tcp'">
|
||||||
<div class="row items-start no-wrap">
|
<div class="row items-start no-wrap">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="text-subtitle2">Pass Host Header</div>
|
<div class="text-subtitle2">Pass Host Header</div>
|
||||||
|
@ -54,6 +54,19 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section v-if="data.loadBalancer.terminationDelay">
|
||||||
|
<div class="row items-start no-wrap">
|
||||||
|
<div class="col">
|
||||||
|
<div class="text-subtitle2">Termination Delay</div>
|
||||||
|
<q-chip
|
||||||
|
dense
|
||||||
|
class="app-chip app-chip-name">
|
||||||
|
{{ data.loadBalancer.terminationDelay }} ms
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
<q-separator v-if="sticky" />
|
<q-separator v-if="sticky" />
|
||||||
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
|
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Helps from '../../_helpers/Helps'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ToolBarTable',
|
name: 'ToolBarTable',
|
||||||
props: ['status', 'filter'],
|
props: ['status', 'filter'],
|
||||||
|
@ -36,6 +38,9 @@ export default {
|
||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
this.routeToState(this.$route)
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getStatus: {
|
getStatus: {
|
||||||
get () {
|
get () {
|
||||||
|
@ -43,6 +48,7 @@ export default {
|
||||||
},
|
},
|
||||||
set (newValue) {
|
set (newValue) {
|
||||||
this.$emit('update:status', newValue)
|
this.$emit('update:status', newValue)
|
||||||
|
this.stateToRoute(this.$route, { status: newValue })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getFilter: {
|
getFilter: {
|
||||||
|
@ -51,11 +57,30 @@ export default {
|
||||||
},
|
},
|
||||||
set (newValue) {
|
set (newValue) {
|
||||||
this.$emit('update:filter', newValue)
|
this.$emit('update:filter', newValue)
|
||||||
|
this.stateToRoute(this.$route, { filter: newValue })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route (to, from) {
|
||||||
|
this.routeToState(to)
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
routeToState (route) {
|
||||||
|
for (const query in route.query) {
|
||||||
|
this.$emit(`update:${query}`, route.query[query])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stateToRoute (route, values) {
|
||||||
|
this.$router.push({
|
||||||
|
path: route.path,
|
||||||
|
query: Helps.removeEmptyObjects({
|
||||||
|
...route.query,
|
||||||
|
...values
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
|
||||||
|
|
|
@ -144,8 +144,8 @@ export default {
|
||||||
loadingOverview: true,
|
loadingOverview: true,
|
||||||
timeOutEntryGetAll: null,
|
timeOutEntryGetAll: null,
|
||||||
timeOutOverviewAll: null,
|
timeOutOverviewAll: null,
|
||||||
intervalRefreshAll: null,
|
intervalRefresh: null,
|
||||||
intervalRefreshAllTime: 5000
|
intervalRefreshTime: 5000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -167,10 +167,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('entrypoints', { entryGetAll: 'getAll' }),
|
...mapActions('entrypoints', { entryGetAll: 'getAll' }),
|
||||||
...mapActions('core', { getOverview: 'getOverview' }),
|
...mapActions('core', { getOverview: 'getOverview' }),
|
||||||
refreshAll () {
|
fetchEntries () {
|
||||||
this.onGetAll()
|
|
||||||
},
|
|
||||||
onGetAll () {
|
|
||||||
this.entryGetAll()
|
this.entryGetAll()
|
||||||
.then(body => {
|
.then(body => {
|
||||||
console.log('Success -> dashboard/entrypoints', body)
|
console.log('Success -> dashboard/entrypoints', body)
|
||||||
|
@ -182,6 +179,8 @@ export default {
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log('Error -> dashboard/entrypoints', error)
|
console.log('Error -> dashboard/entrypoints', error)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fetchOverview () {
|
||||||
this.getOverview()
|
this.getOverview()
|
||||||
.then(body => {
|
.then(body => {
|
||||||
console.log('Success -> dashboard/overview', body)
|
console.log('Success -> dashboard/overview', body)
|
||||||
|
@ -193,16 +192,18 @@ export default {
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log('Error -> dashboard/overview', error)
|
console.log('Error -> dashboard/overview', error)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fetchAll () {
|
||||||
|
this.fetchEntries()
|
||||||
|
this.fetchOverview()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.refreshAll()
|
this.fetchAll()
|
||||||
this.intervalRefreshAll = setInterval(() => {
|
this.intervalRefresh = setInterval(this.fetchOverview, this.intervalRefreshTime)
|
||||||
this.refreshAll()
|
|
||||||
}, this.intervalRefreshAllTime)
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
clearInterval(this.intervalRefreshAll)
|
clearInterval(this.intervalRefresh)
|
||||||
clearTimeout(this.timeOutEntryGetAll)
|
clearTimeout(this.timeOutEntryGetAll)
|
||||||
clearTimeout(this.timeOutOverviewAll)
|
clearTimeout(this.timeOutOverviewAll)
|
||||||
this.$store.commit('entrypoints/getAllClear')
|
this.$store.commit('entrypoints/getAllClear')
|
||||||
|
|
Loading…
Reference in a new issue