From e5104021b1b96735782463f32d476944856af05c Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 19 Nov 2019 10:18:05 +0100 Subject: [PATCH 01/14] doc: remove double quotes on CLI flags. --- docs/content/https/acme.md | 16 ++--- docs/content/https/ref-acme.txt | 8 +-- docs/content/migration/v1-to-v2.md | 62 +++++++++---------- docs/content/observability/access-logs.md | 26 ++++---- docs/content/observability/logs.md | 8 +-- docs/content/observability/metrics/datadog.md | 2 +- .../content/observability/metrics/influxdb.md | 28 ++++----- .../observability/metrics/prometheus.md | 4 +- docs/content/observability/metrics/statsd.md | 2 +- docs/content/observability/tracing/datadog.md | 4 +- .../content/observability/tracing/haystack.md | 10 +-- docs/content/observability/tracing/instana.md | 4 +- docs/content/observability/tracing/jaeger.md | 18 +++--- .../content/observability/tracing/overview.md | 2 +- docs/content/observability/tracing/zipkin.md | 4 +- docs/content/operations/ping.md | 4 +- docs/content/providers/docker.md | 14 ++--- docs/content/providers/kubernetes-crd.md | 12 ++-- docs/content/providers/kubernetes-ingress.md | 18 +++--- docs/content/providers/marathon.md | 20 +++--- docs/content/providers/rancher.md | 10 +-- docs/content/providers/rancher.txt | 2 +- docs/content/providers/rancher.yml | 2 +- docs/content/routing/entrypoints.md | 4 +- docs/content/routing/overview.md | 2 +- docs/content/routing/providers/docker.md | 2 +- docs/content/routing/routers/index.md | 28 ++++----- docs/content/user-guides/grpc.md | 4 +- 28 files changed, 160 insertions(+), 160 deletions(-) diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 80e48d1d6..d23cf4317 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -47,11 +47,11 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom ``` ```bash tab="CLI" - --entryPoints.web.address=":80" - --entryPoints.websecure.address=":443" + --entryPoints.web.address=:80 + --entryPoints.websecure.address=:443 # ... - --certificatesResolvers.sample.acme.email="your-email@your-domain.org" - --certificatesResolvers.sample.acme.storage="acme.json" + --certificatesResolvers.sample.acme.email=your-email@your-domain.org + --certificatesResolvers.sample.acme.storage=acme.json # used during the challenge --certificatesResolvers.sample.acme.httpChallenge.entryPoint=web ``` @@ -156,8 +156,8 @@ when using the `HTTP-01` challenge, `certificatesResolvers.sample.acme.httpChall ``` ```bash tab="CLI" - --entryPoints.web.address=":80" - --entryPoints.websecure.address=":443" + --entryPoints.web.address=:80 + --entryPoints.websecure.address=:443 # ... --certificatesResolvers.sample.acme.httpChallenge.entryPoint=web ``` @@ -312,7 +312,7 @@ certificatesResolvers: ```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 @@ -342,7 +342,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi ```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 # ... ``` diff --git a/docs/content/https/ref-acme.txt b/docs/content/https/ref-acme.txt index d71023048..89431729b 100644 --- a/docs/content/https/ref-acme.txt +++ b/docs/content/https/ref-acme.txt @@ -4,13 +4,13 @@ # # Required # ---certificatesResolvers.sample.acme.email="test@traefik.io" +--certificatesResolvers.sample.acme.email=test@traefik.io # File or key used for certificates storage. # # Required # ---certificatesResolvers.sample.acme.storage="acme.json" +--certificatesResolvers.sample.acme.storage=acme.json # CA server to use. # Uncomment the line to use Let's Encrypt's staging server, @@ -19,7 +19,7 @@ # Optional # 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. # @@ -75,7 +75,7 @@ # Optional # 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. # diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md index c56056d41..429eb7cc8 100644 --- a/docs/content/migration/v1-to-v2.md +++ b/docs/content/migration/v1-to-v2.md @@ -718,11 +718,11 @@ with the path `/admin` stripped, e.g. to `http://:/`. In this case, yo ``` ```bash tab="CLI" - --entryPoints.web.address=":80" - --entryPoints.websecure.address=":443" - --certificatesResolvers.sample.acme.email: your-email@your-domain.org - --certificatesResolvers.sample.acme.storage: acme.json - --certificatesResolvers.sample.acme.httpChallenge.entryPoint: web + --entryPoints.web.address=:80 + --entryPoints.websecure.address=:443 + --certificatesResolvers.sample.acme.email=your-email@your-domain.org + --certificatesResolvers.sample.acme.storage=acme.json + --certificatesResolvers.sample.acme.httpChallenge.entryPoint=web ``` ## Traefik Logs @@ -744,9 +744,9 @@ There is no more log configuration at the root level. ``` ```bash tab="CLI" - --logLevel="DEBUG" - --traefikLog.filePath="/path/to/traefik.log" - --traefikLog.format="json" + --logLevel=DEBUG + --traefikLog.filePath=/path/to/traefik.log + --traefikLog.format=json ``` !!! info "v2" @@ -768,9 +768,9 @@ There is no more log configuration at the root level. ``` ```bash tab="CLI" - --log.level="DEBUG" - --log.filePath="/path/to/traefik.log" - --log.format="json" + --log.level=DEBUG + --log.filePath=/path/to/traefik.log + --log.format=json ``` ## Tracing @@ -794,12 +794,12 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is ``` ```bash tab="CLI" - --tracing.backend="jaeger" - --tracing.servicename="tracing" - --tracing.jaeger.localagenthostport="12.0.0.1:6831" - --tracing.jaeger.samplingparam="1.0" - --tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling" - --tracing.jaeger.samplingtype="const" + --tracing.backend=jaeger + --tracing.servicename=tracing + --tracing.jaeger.localagenthostport=12.0.0.1:6831 + --tracing.jaeger.samplingparam=1.0 + --tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling + --tracing.jaeger.samplingtype=const ``` !!! info "v2" @@ -827,11 +827,11 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is ``` ```bash tab="CLI" - --tracing.servicename="tracing" - --tracing.jaeger.localagenthostport="12.0.0.1:6831" - --tracing.jaeger.samplingparam="1.0" - --tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling" - --tracing.jaeger.samplingtype="const" + --tracing.servicename=tracing + --tracing.jaeger.localagenthostport=12.0.0.1:6831 + --tracing.jaeger.samplingparam=1.0 + --tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling + --tracing.jaeger.samplingtype=const ``` ## Metrics @@ -852,7 +852,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/ ```bash tab="CLI" --metrics.prometheus.buckets=[0.1,0.3,1.2,5.0] - --metrics.prometheus.entrypoint="traefik" + --metrics.prometheus.entrypoint=traefik ``` !!! info "v2" @@ -878,7 +878,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/ ```bash tab="CLI" --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 @@ -908,14 +908,14 @@ Each root item has been moved to a related section or removed. ```bash tab="CLI" --checknewversion=false --sendanonymoususage=true - --loglevel="DEBUG" + --loglevel=DEBUG --insecureskipverify=true - --rootcas="/mycert.cert" + --rootcas=/mycert.cert --maxidleconnsperhost=200 - --providersthrottleduration="2s" + --providersthrottleduration=2s --allowminweightzero=true --debug=true - --defaultentrypoints="web","web-secure" + --defaultentrypoints=web,web-secure --keeptrailingslash=true ``` @@ -961,9 +961,9 @@ Each root item has been moved to a related section or removed. ```bash tab="CLI" --global.checknewversion=true --global.sendanonymoususage=true - --log.level="DEBUG" + --log.level=DEBUG --serverstransport.insecureskipverify=true - --serverstransport.rootcas="/mycert.cert" + --serverstransport.rootcas=/mycert.cert --serverstransport.maxidleconnsperhost=42 --providers.providersthrottleduration=42 ``` @@ -1029,7 +1029,7 @@ As the dashboard access is now secured by default you can either: [api] [providers.file] - filename = "/dynamic-conf.toml" + filename = "/dynamic-conf.toml" ##---------------------## diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 5a27efc17..234204ed3 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -61,7 +61,7 @@ accessLog: ```bash tab="CLI" # Configuring a buffer of 100 lines --accesslog=true ---accesslog.filepath="/path/to/access.log" +--accesslog.filepath=/path/to/access.log --accesslog.bufferingsize=100 ``` @@ -104,11 +104,11 @@ accessLog: ```bash tab="CLI" # Configuring Multiple Filters --accesslog=true ---accesslog.filepath="/path/to/access.log" ---accesslog.format="json" ---accesslog.filters.statuscodes="200, 300-302" +--accesslog.filepath=/path/to/access.log +--accesslog.format=json +--accesslog.filters.statuscodes=200,300-302 --accesslog.filters.retryattempts ---accesslog.filters.minduration="10ms" +--accesslog.filters.minduration=10ms ``` ### Limiting the Fields @@ -164,14 +164,14 @@ accessLog: ```bash tab="CLI" # Limiting the Logs to Specific Fields --accesslog=true ---accesslog.filepath="/path/to/access.log" ---accesslog.format="json" ---accesslog.fields.defaultmode="keep" ---accesslog.fields.names.ClientUsername="drop" ---accesslog.fields.headers.defaultmode="keep" ---accesslog.fields.headers.names.User-Agent="redact" ---accesslog.fields.headers.names.Authorization="drop" ---accesslog.fields.headers.names.Content-Type="keep" +--accesslog.filepath=/path/to/access.log +--accesslog.format=json +--accesslog.fields.defaultmode=keep +--accesslog.fields.names.ClientUsername=drop +--accesslog.fields.headers.defaultmode=keep +--accesslog.fields.headers.names.User-Agent=redact +--accesslog.fields.headers.names.Authorization=drop +--accesslog.fields.headers.names.Content-Type=keep ``` ??? info "Available Fields" diff --git a/docs/content/observability/logs.md b/docs/content/observability/logs.md index 37e002f4a..3aa0d6c15 100644 --- a/docs/content/observability/logs.md +++ b/docs/content/observability/logs.md @@ -30,7 +30,7 @@ log: ```bash tab="CLI" # Writing Logs to a File ---log.filePath="/path/to/traefik.log" +--log.filePath=/path/to/traefik.log ``` #### `format` @@ -53,8 +53,8 @@ log: ```bash tab="CLI" # Writing Logs to a File, in JSON ---log.filePath="/path/to/traefik.log" ---log.format="json" +--log.filePath=/path/to/traefik.log +--log.format=json ``` #### `level` @@ -72,7 +72,7 @@ log: ``` ```bash tab="CLI" ---log.level="DEBUG" +--log.level=DEBUG ``` ## Log Rotation diff --git a/docs/content/observability/metrics/datadog.md b/docs/content/observability/metrics/datadog.md index cb412a383..b7763ec5c 100644 --- a/docs/content/observability/metrics/datadog.md +++ b/docs/content/observability/metrics/datadog.md @@ -35,7 +35,7 @@ metrics: ``` ```bash tab="CLI" ---metrics.datadog.address="127.0.0.1:8125" +--metrics.datadog.address=127.0.0.1:8125 ``` #### `addEntryPointsLabels` diff --git a/docs/content/observability/metrics/influxdb.md b/docs/content/observability/metrics/influxdb.md index c30fd44b3..6ffa760b3 100644 --- a/docs/content/observability/metrics/influxdb.md +++ b/docs/content/observability/metrics/influxdb.md @@ -35,7 +35,7 @@ metrics: ``` ```bash tab="CLI" ---metrics.influxdb.address="localhost:8089" +--metrics.influxdb.address=localhost:8089 ``` #### `protocol` @@ -57,7 +57,7 @@ metrics: ``` ```bash tab="CLI" ---metrics.influxdb.protocol="udp" +--metrics.influxdb.protocol=udp ``` #### `database` @@ -69,17 +69,17 @@ InfluxDB database used when protocol is http. ```toml tab="File (TOML)" [metrics] [metrics.influxDB] - database = "" + database = "db" ``` ```yaml tab="File (YAML)" metrics: influxDB: - database: "" + database: "db" ``` ```bash tab="CLI" ---metrics.influxdb.database="" +--metrics.influxdb.database=db ``` #### `retentionPolicy` @@ -91,17 +91,17 @@ InfluxDB retention policy used when protocol is http. ```toml tab="File (TOML)" [metrics] [metrics.influxDB] - retentionPolicy = "" + retentionPolicy = "two_hours" ``` ```yaml tab="File (YAML)" metrics: influxDB: - retentionPolicy: "" + retentionPolicy: "two_hours" ``` ```bash tab="CLI" ---metrics.influxdb.retentionPolicy="" +--metrics.influxdb.retentionPolicy=two_hours ``` #### `username` @@ -113,17 +113,17 @@ InfluxDB username (only with http). ```toml tab="File (TOML)" [metrics] [metrics.influxDB] - username = "" + username = "john" ``` ```yaml tab="File (YAML)" metrics: influxDB: - username: "" + username: "john" ``` ```bash tab="CLI" ---metrics.influxdb.username="" +--metrics.influxdb.username=john ``` #### `password` @@ -135,17 +135,17 @@ InfluxDB password (only with http). ```toml tab="File (TOML)" [metrics] [metrics.influxDB] - password = "" + password = "secret" ``` ```yaml tab="File (YAML)" metrics: influxDB: - password: "" + password: "secret" ``` ```bash tab="CLI" ---metrics.influxdb.password="" +--metrics.influxdb.password=secret ``` #### `addEntryPointsLabels` diff --git a/docs/content/observability/metrics/prometheus.md b/docs/content/observability/metrics/prometheus.md index fcb95bd1c..e03a0a914 100644 --- a/docs/content/observability/metrics/prometheus.md +++ b/docs/content/observability/metrics/prometheus.md @@ -113,6 +113,6 @@ metrics: ``` ```bash tab="CLI" ---entryPoints.metrics.address=":8082" ---metrics.prometheus.entryPoint="metrics" +--entryPoints.metrics.address=:8082 +--metrics.prometheus.entryPoint=metrics ``` diff --git a/docs/content/observability/metrics/statsd.md b/docs/content/observability/metrics/statsd.md index a777ae74f..e5f1a58b5 100644 --- a/docs/content/observability/metrics/statsd.md +++ b/docs/content/observability/metrics/statsd.md @@ -35,7 +35,7 @@ metrics: ``` ```bash tab="CLI" ---metrics.statsd.address="localhost:8125" +--metrics.statsd.address=localhost:8125 ``` #### `addEntryPointsLabels` diff --git a/docs/content/observability/tracing/datadog.md b/docs/content/observability/tracing/datadog.md index 844f4fc70..a6ce77d5d 100644 --- a/docs/content/observability/tracing/datadog.md +++ b/docs/content/observability/tracing/datadog.md @@ -35,7 +35,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.datadog.localAgentHostPort="127.0.0.1:8126" +--tracing.datadog.localAgentHostPort=127.0.0.1:8126 ``` #### `debug` @@ -79,7 +79,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.datadog.globalTag="sample" +--tracing.datadog.globalTag=sample ``` #### `prioritySampling` diff --git a/docs/content/observability/tracing/haystack.md b/docs/content/observability/tracing/haystack.md index eb32bea21..ae0676c70 100644 --- a/docs/content/observability/tracing/haystack.md +++ b/docs/content/observability/tracing/haystack.md @@ -35,7 +35,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.haystack.localAgentHost="127.0.0.1" +--tracing.haystack.localAgentHost=127.0.0.1 ``` #### `localAgentPort` @@ -79,7 +79,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.haystack.globalTag="sample:test" +--tracing.haystack.globalTag=sample:test ``` #### `traceIDHeaderName` @@ -101,7 +101,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.haystack.traceIDHeaderName="sample" +--tracing.haystack.traceIDHeaderName=sample ``` #### `parentIDHeaderName` @@ -123,7 +123,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.haystack.parentIDHeaderName="sample" +--tracing.haystack.parentIDHeaderName=sample ``` #### `spanIDHeaderName` @@ -168,5 +168,5 @@ tracing: ```bash tab="CLI" ---tracing.haystack.baggagePrefixHeaderName="sample" +--tracing.haystack.baggagePrefixHeaderName=sample ``` diff --git a/docs/content/observability/tracing/instana.md b/docs/content/observability/tracing/instana.md index b4e5d78e1..bc6b69958 100644 --- a/docs/content/observability/tracing/instana.md +++ b/docs/content/observability/tracing/instana.md @@ -35,7 +35,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.instana.localAgentHost="127.0.0.1" +--tracing.instana.localAgentHost=127.0.0.1 ``` #### `localAgentPort` @@ -86,5 +86,5 @@ tracing: ``` ```bash tab="CLI" ---tracing.instana.logLevel="info" +--tracing.instana.logLevel=info ``` diff --git a/docs/content/observability/tracing/jaeger.md b/docs/content/observability/tracing/jaeger.md index a95975db1..af4397e05 100644 --- a/docs/content/observability/tracing/jaeger.md +++ b/docs/content/observability/tracing/jaeger.md @@ -39,7 +39,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.samplingServerURL="http://localhost:5778/sampling" +--tracing.jaeger.samplingServerURL=http://localhost:5778/sampling ``` #### `samplingType` @@ -61,7 +61,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.samplingType="const" +--tracing.jaeger.samplingType=const ``` #### `samplingParam` @@ -89,7 +89,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.samplingParam="1.0" +--tracing.jaeger.samplingParam=1.0 ``` #### `localAgentHostPort` @@ -111,7 +111,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.localAgentHostPort="127.0.0.1:6831" +--tracing.jaeger.localAgentHostPort=127.0.0.1:6831 ``` #### `gen128Bit` @@ -159,7 +159,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.propagation="jaeger" +--tracing.jaeger.propagation=jaeger ``` #### `traceContextHeaderName` @@ -182,7 +182,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.traceContextHeaderName="uber-trace-id" +--tracing.jaeger.traceContextHeaderName=uber-trace-id ``` ### `collector` @@ -206,7 +206,7 @@ tracing: ``` ```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` @@ -229,7 +229,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.collector.user="my-user" +--tracing.jaeger.collector.user=my-user ``` #### `password` @@ -252,5 +252,5 @@ tracing: ``` ```bash tab="CLI" ---tracing.jaeger.collector.password="my-password" +--tracing.jaeger.collector.password=my-password ``` diff --git a/docs/content/observability/tracing/overview.md b/docs/content/observability/tracing/overview.md index a6cb8dbd8..8098cd0ea 100644 --- a/docs/content/observability/tracing/overview.md +++ b/docs/content/observability/tracing/overview.md @@ -52,7 +52,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.serviceName="traefik" +--tracing.serviceName=traefik ``` #### `spanNameLimit` diff --git a/docs/content/observability/tracing/zipkin.md b/docs/content/observability/tracing/zipkin.md index 8aa1c0283..dcd76323e 100644 --- a/docs/content/observability/tracing/zipkin.md +++ b/docs/content/observability/tracing/zipkin.md @@ -35,7 +35,7 @@ tracing: ``` ```bash tab="CLI" ---tracing.zipkin.httpEndpoint="http://localhost:9411/api/v2/spans" +--tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans ``` #### `sameSpan` @@ -101,5 +101,5 @@ tracing: ``` ```bash tab="CLI" ---tracing.zipkin.sampleRate="0.2" +--tracing.zipkin.sampleRate=0.2 ``` diff --git a/docs/content/operations/ping.md b/docs/content/operations/ping.md index b0640207b..b072adcee 100644 --- a/docs/content/operations/ping.md +++ b/docs/content/operations/ping.md @@ -55,6 +55,6 @@ ping: ``` ```bash tab="CLI" ---entryPoints.ping.address=":8082" ---ping.entryPoint="ping" +--entryPoints.ping.address=:8082 +--ping.entryPoint=ping ``` diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index af39fc68b..6c497c809 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -64,7 +64,7 @@ Attach labels to your containers and let Traefik do the rest! ``` ```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 ``` @@ -108,7 +108,7 @@ providers: ``` ```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. @@ -186,7 +186,7 @@ Traefik requires access to the docker socket to get its dynamic configuration. ``` ```bash tab="CLI" - --providers.docker.endpoint="unix:///var/run/docker.sock" + --providers.docker.endpoint=unix:///var/run/docker.sock # ... ``` @@ -311,7 +311,7 @@ providers: ``` ```bash tab="CLI" ---providers.docker.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)" +--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`) # ... ``` @@ -375,19 +375,19 @@ _Optional, Default=""_ ```toml tab="File (TOML)" [providers.docker] - constraints = "Label(`a.label.name`, `foo`)" + constraints = "Label(`a.label.name`,`foo`)" # ... ``` ```yaml tab="File (YAML)" providers: docker: - constraints: "Label(`a.label.name`, `foo`)" + constraints: "Label(`a.label.name`,`foo`)" # ... ``` ```bash tab="CLI" ---providers.docker.constraints="Label(`a.label.name`, `foo`)" +--providers.docker.constraints=Label(`a.label.name`,`foo`) # ... ``` diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index 5be3c8cff..c992d3164 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -32,7 +32,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetescrd.endpoint="http://localhost:8080" +--providers.kubernetescrd.endpoint=http://localhost:8080 ``` The Kubernetes server endpoint as URL. @@ -66,7 +66,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetescrd.token="mytoken" +--providers.kubernetescrd.token=mytoken ``` Bearer token used for the Kubernetes client configuration. @@ -89,7 +89,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetescrd.certauthfilepath="/my/ca.crt" +--providers.kubernetescrd.certauthfilepath=/my/ca.crt ``` Path to the certificate authority file. @@ -115,7 +115,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetescrd.namespaces="default,production" +--providers.kubernetescrd.namespaces=default,production ``` Array of namespaces to watch. @@ -164,7 +164,7 @@ providers: ``` ```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. @@ -190,7 +190,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetescrd.throttleDuration="10s" +--providers.kubernetescrd.throttleDuration=10s ``` ## Further diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index 9bf6f34af..95aeeeda3 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -67,7 +67,7 @@ providers: ``` ```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. @@ -99,7 +99,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.token="mytoken" +--providers.kubernetesingress.token=mytoken ``` Bearer token used for the Kubernetes client configuration. @@ -122,7 +122,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.certauthfilepath="/my/ca.crt" +--providers.kubernetesingress.certauthfilepath=/my/ca.crt ``` Path to the certificate authority file. @@ -171,7 +171,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.namespaces="default,production" +--providers.kubernetesingress.namespaces=default,production ``` Array of namespaces to watch. @@ -220,7 +220,7 @@ providers: ``` ```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. @@ -249,7 +249,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.ingressendpoint.hostname="foo.com" +--providers.kubernetesingress.ingressendpoint.hostname=foo.com ``` Hostname used for Kubernetes Ingress endpoints. @@ -273,7 +273,7 @@ providers: ``` ```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. @@ -297,7 +297,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.ingressendpoint.publishedservice="foo-service" +--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service ``` Published Kubernetes Service to copy status from. @@ -320,7 +320,7 @@ providers: ``` ```bash tab="CLI" ---providers.kubernetesingress.throttleDuration="10s" +--providers.kubernetesingress.throttleDuration=10s ``` ## Further diff --git a/docs/content/providers/marathon.md b/docs/content/providers/marathon.md index fe288fbd8..7fcf3ac49 100644 --- a/docs/content/providers/marathon.md +++ b/docs/content/providers/marathon.md @@ -74,8 +74,8 @@ providers: ``` ```bash tab="CLI" ---providers.marathon.basic.httpbasicauthuser="foo" ---providers.marathon.basic.httpbasicpassword="bar" +--providers.marathon.basic.httpbasicauthuser=foo +--providers.marathon.basic.httpbasicpassword=bar ``` Enables Marathon basic authentication. @@ -98,7 +98,7 @@ providers: ``` ```bash tab="CLI" ---providers.marathon.dcosToken="xxxxxx" +--providers.marathon.dcosToken=xxxxxx ``` DCOSToken for DCOS environment. @@ -123,7 +123,7 @@ providers: ``` ```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" ---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. @@ -223,19 +223,19 @@ _Optional, Default=""_ ```toml tab="File (TOML)" [providers.marathon] - constraints = "Label(`a.label.name`, `foo`)" + constraints = "Label(`a.label.name`,`foo`)" # ... ``` ```yaml tab="File (YAML)" providers: marathon: - constraints: "Label(`a.label.name`, `foo`)" + constraints: "Label(`a.label.name`,`foo`)" # ... ``` ```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" ---providers.marathon.responseHeaderTimeout="66s" +--providers.marathon.responseHeaderTimeout=66s # ... ``` @@ -532,7 +532,7 @@ providers: ``` ```bash tab="CLI" ---providers.marathon.responseHeaderTimeout="10s" +--providers.marathon.responseHeaderTimeout=10s # ... ``` diff --git a/docs/content/providers/rancher.md b/docs/content/providers/rancher.md index 89dc4c6fb..d9d5b4106 100644 --- a/docs/content/providers/rancher.md +++ b/docs/content/providers/rancher.md @@ -104,7 +104,7 @@ providers: ``` ```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" ---providers.rancher.prefix="/test" +--providers.rancher.prefix=/test # ... ``` @@ -221,19 +221,19 @@ _Optional, Default=""_ ```toml tab="File (TOML)" [providers.rancher] - constraints = "Label(`a.label.name`, `foo`)" + constraints = "Label(`a.label.name`,`foo`)" # ... ``` ```yaml tab="File (YAML)" providers: rancher: - constraints: "Label(`a.label.name`, `foo`)" + constraints: "Label(`a.label.name`,`foo`)" # ... ``` ```bash tab="CLI" ---providers.rancher.constraints="Label(`a.label.name`, `foo`)" +--providers.rancher.constraints=Label(`a.label.name`,`foo`) # ... ``` diff --git a/docs/content/providers/rancher.txt b/docs/content/providers/rancher.txt index be28f4d99..158826cda 100644 --- a/docs/content/providers/rancher.txt +++ b/docs/content/providers/rancher.txt @@ -17,4 +17,4 @@ --providers.rancher.intervalPoll=false # Prefix used for accessing the Rancher metadata service ---providers.rancher.prefix="/latest" +--providers.rancher.prefix=/latest diff --git a/docs/content/providers/rancher.yml b/docs/content/providers/rancher.yml index bc9d57fff..227b352c3 100644 --- a/docs/content/providers/rancher.yml +++ b/docs/content/providers/rancher.yml @@ -18,4 +18,4 @@ providers: intervalPoll: false # Prefix used for accessing the Rancher metadata service - prefix: "/latest" + prefix: /latest diff --git a/docs/content/routing/entrypoints.md b/docs/content/routing/entrypoints.md index 1f2354acb..12283dcd7 100644 --- a/docs/content/routing/entrypoints.md +++ b/docs/content/routing/entrypoints.md @@ -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.idleTimeout=42 --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.trustedIPs="127.0.0.1,192.168.0.1" + --entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1 ``` ### Forwarded Header diff --git a/docs/content/routing/overview.md b/docs/content/routing/overview.md index c18e1ad3a..8808e0a48 100644 --- a/docs/content/routing/overview.md +++ b/docs/content/routing/overview.md @@ -151,7 +151,7 @@ http: ```bash tab="CLI" # 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 --providers.file.filename=dynamic_conf.toml diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index 7af257d6b..f57d5ef7d 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -82,7 +82,7 @@ Attach labels to your containers and let Traefik do the rest! ``` ```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 ``` diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 773b71d16..523eb97af 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -78,8 +78,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie ```bash tab="CLI" ## Static configuration - --entryPoints.web.address=":80" - --entryPoints.mysql.address=":3306" + --entryPoints.web.address=:80 + --entryPoints.mysql.address=:3306 ``` ## 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" ## Static configuration - --entrypoints.web.address=":80" - --entrypoints.websecure.address=":443" - --entrypoints.other.address=":9090" + --entrypoints.web.address=:80 + --entrypoints.websecure.address=:443 + --entrypoints.other.address=:9090 ``` ??? 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" ## Static configuration - --entrypoints.web.address=":80" - --entrypoints.websecure.address=":443" - --entrypoints.other.address=":9090" + --entrypoints.web.address=:80 + --entrypoints.websecure.address=:443 + --entrypoints.other.address=:9090 ``` ### 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" ## Static configuration - --entrypoints.web.address=":80" - --entrypoints.websecure.address=":443" - --entrypoints.other.address=":9090" + --entrypoints.web.address=:80 + --entrypoints.websecure.address=:443 + --entrypoints.other.address=:9090 ``` ??? 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" ## Static configuration - --entrypoints.web.address=":80" - --entrypoints.websecure.address=":443" - --entrypoints.other.address=":9090" + --entrypoints.web.address=:80 + --entrypoints.websecure.address=:443 + --entrypoints.other.address=:9090 ``` ### Rule diff --git a/docs/content/user-guides/grpc.md b/docs/content/user-guides/grpc.md index 271dc0468..6c8979dec 100644 --- a/docs/content/user-guides/grpc.md +++ b/docs/content/user-guides/grpc.md @@ -32,7 +32,7 @@ api: {} ``` ```yaml tab="CLI" ---entryPoints.web.address=":80" +--entryPoints.web.address=:80 --providers.file.filename=dynamic_conf.toml --api.insecure=true ``` @@ -153,7 +153,7 @@ api: {} ``` ```yaml tab="CLI" ---entryPoints.websecure.address=":4443" +--entryPoints.websecure.address=:4443 # For secure connection on backend.local --serversTransport.rootCAs=./backend.cert --providers.file.filename=dynamic_conf.toml From 9761161163dc87e976f702919abaaddfc45d4569 Mon Sep 17 00:00:00 2001 From: Matthieu Hostache Date: Wed, 20 Nov 2019 18:26:10 +0100 Subject: [PATCH 02/14] Web UI: Fix displayed tcp service details --- .../components/_commons/PanelServiceDetails.vue | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/webui/src/components/_commons/PanelServiceDetails.vue b/webui/src/components/_commons/PanelServiceDetails.vue index 9bde9603d..d996b9ba2 100644 --- a/webui/src/components/_commons/PanelServiceDetails.vue +++ b/webui/src/components/_commons/PanelServiceDetails.vue @@ -45,7 +45,7 @@ - +
Pass Host Header
@@ -54,6 +54,19 @@
+ +
+
+
Termination Delay
+ + {{ data.loadBalancer.terminationDelay }} ms + +
+
+
+ From abdb3b9475b6a30a869c06cbbb6b259b9153c506 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou Date: Wed, 20 Nov 2019 18:34:05 +0100 Subject: [PATCH 03/14] Uses, if it exists, the ping entry point provided in the static configuration Co-authored-by: Ludovic Fernandez --- cmd/healthcheck/healthcheck.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/healthcheck/healthcheck.go b/cmd/healthcheck/healthcheck.go index bf1c2762f..0a922b2a0 100644 --- a/cmd/healthcheck/healthcheck.go +++ b/cmd/healthcheck/healthcheck.go @@ -51,9 +51,14 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) { 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 { - return nil, errors.New("missing `ping` entrypoint") + return nil, fmt.Errorf("ping: missing %s entry point", ep) } client := &http.Client{Timeout: 5 * time.Second} From 00db3a0922a378a2f71c911bd1bad3035920ce24 Mon Sep 17 00:00:00 2001 From: Matthieu Hostache Date: Sat, 23 Nov 2019 23:18:04 +0100 Subject: [PATCH 04/14] Web UI: Avoid some router properties to overflow their container --- webui/src/components/_commons/PanelRouterDetails.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webui/src/components/_commons/PanelRouterDetails.vue b/webui/src/components/_commons/PanelRouterDetails.vue index b5c2aec2a..cb53987ce 100644 --- a/webui/src/components/_commons/PanelRouterDetails.vue +++ b/webui/src/components/_commons/PanelRouterDetails.vue @@ -27,7 +27,7 @@
RULE
+ class="app-chip app-chip-wrap app-chip-rule"> {{ data.rule }}
@@ -39,7 +39,7 @@
NAME
+ class="app-chip app-chip-wrap app-chip-name"> {{ data.name }} @@ -66,7 +66,7 @@ dense clickable @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 }} From 5b1dc0bfbd963ccbdcf0020fd3f8082e5297772e Mon Sep 17 00:00:00 2001 From: Manuel Zapf Date: Wed, 27 Nov 2019 11:12:07 +0100 Subject: [PATCH 05/14] Change service name in rancher provider to make webui service details view work --- pkg/provider/rancher/rancher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/rancher/rancher.go b/pkg/provider/rancher/rancher.go index f69d0cbde..20f274e60 100644 --- a/pkg/provider/rancher/rancher.go +++ b/pkg/provider/rancher/rancher.go @@ -193,7 +193,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [ } service := rancherData{ - Name: service.Name + "/" + stack.Name, + Name: service.Name + "_" + stack.Name, State: service.State, Labels: service.Labels, Port: servicePort, From 407eda0ba0eb8f67814b13fb6b3472e78ebf5797 Mon Sep 17 00:00:00 2001 From: Matthieu Hostache Date: Wed, 27 Nov 2019 12:04:05 +0100 Subject: [PATCH 06/14] Web UI: Avoid unnecessary duplicated api calls --- webui/src/_services/HttpService.js | 33 +++++++++++------------------- webui/src/_services/TcpService.js | 22 ++++++++------------ 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/webui/src/_services/HttpService.js b/webui/src/_services/HttpService.js index 19deca0b6..48010c1e0 100644 --- a/webui/src/_services/HttpService.js +++ b/webui/src/_services/HttpService.js @@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP' const apiBase = '/http' function getAllRouters (params) { - return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`) + return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`) .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}`) - .then(body => { - console.log('Success -> HttpService -> getAllRouters', body.data) - // TODO - suggestion: add the total-pages in api response to optimize the query - return { data: body.data || [], total } - }) + console.log('Success -> HttpService -> getAllRouters', body.data) + // TODO - suggestion: add the total-pages in api response to optimize the query + return { data: body.data || [], total } }) } @@ -24,15 +21,12 @@ function getRouterByName (name) { } function getAllServices (params) { - return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`) + return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`) .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}`) - .then(body => { - console.log('Success -> HttpService -> getAllServices', body.data) - // TODO - suggestion: add the total-pages in api response to optimize the query - return { data: body.data || [], total } - }) + console.log('Success -> HttpService -> getAllServices', body.data) + // TODO - suggestion: add the total-pages in api response to optimize the query + return { data: body.data || [], total } }) } @@ -45,15 +39,12 @@ function getServiceByName (name) { } function getAllMiddlewares (params) { - return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`) + return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`) .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}`) - .then(body => { - console.log('Success -> HttpService -> getAllMiddlewares', body.data) - // TODO - suggestion: add the total-pages in api response to optimize the query - return { data: body.data || [], total } - }) + console.log('Success -> HttpService -> getAllMiddlewares', body.data) + // TODO - suggestion: add the total-pages in api response to optimize the query + return { data: body.data || [], total } }) } diff --git a/webui/src/_services/TcpService.js b/webui/src/_services/TcpService.js index 08f1b6131..694ce099c 100644 --- a/webui/src/_services/TcpService.js +++ b/webui/src/_services/TcpService.js @@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP' const apiBase = '/tcp' function getAllRouters (params) { - return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`) + return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`) .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}`) - .then(body => { - console.log('Success -> HttpService -> getAllRouters', body.data) - // TODO - suggestion: add the total-pages in api response to optimize the query - return { data: body.data || [], total } - }) + console.log('Success -> HttpService -> getAllRouters', body.data) + // TODO - suggestion: add the total-pages in api response to optimize the query + return { data: body.data || [], total } }) } @@ -24,15 +21,12 @@ function getRouterByName (name) { } function getAllServices (params) { - return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`) + return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`) .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}`) - .then(body => { - console.log('Success -> HttpService -> getAllServices', body.data) - // TODO - suggestion: add the total-pages in api response to optimize the query - return { data: body.data || [], total } - }) + console.log('Success -> HttpService -> getAllServices', body.data) + // TODO - suggestion: add the total-pages in api response to optimize the query + return { data: body.data || [], total } }) } From ba490124478010027b752382e74f49fd1e1d3607 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Wed, 27 Nov 2019 16:02:05 +0100 Subject: [PATCH 07/14] Mention the experimental Helm Chart in the installation section of documentation --- .../getting-started/install-traefik.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index 96cf721b1..2cda421f0 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -3,6 +3,7 @@ You can install Traefik with the following flavors: * [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) * [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). * 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 . + +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 Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page. From 2685e0652892cbad2c7f6e59d86a595d47ee88f5 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Wed, 27 Nov 2019 17:12:04 +0100 Subject: [PATCH 08/14] Add Swarm section to the Docker Provider Documentation --- docs/content/providers/docker.md | 183 ++++++++++++++++------- docs/content/routing/providers/docker.md | 5 +- 2 files changed, 136 insertions(+), 52 deletions(-) diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 6c497c809..5310a78e4 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -7,6 +7,9 @@ A Story of Labels & Containers 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" 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! @@ -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 ``` - !!! 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 -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..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..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 @@ -111,48 +235,7 @@ providers: --providers.docker.endpoint=unix:///var/run/docker.sock ``` -Traefik requires access to the docker socket to get its dynamic configuration. - -??? 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. +See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API Access](#docker-api-access_1) for more information. ??? example "Using the docker.sock" @@ -343,7 +426,7 @@ providers: # ... ``` -Activates the Swarm Mode. +Activates the Swarm Mode (instead of standalone Docker). ### `swarmModeRefreshSeconds` diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index f57d5ef7d..f1f2938e1 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -243,11 +243,12 @@ you'd add the label `traefik.http.services..loadbalancer.pa !!! warning "The character `@` is not authorized in the service name ``." ??? info "`traefik.http.services..loadbalancer.server.port`" - + Registers a port. 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 - "traefik.http.services.myservice.loadbalancer.server.port=8080" From b2c59be8de0577bad2512facfa7f63b10b71388f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=9A=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Wed, 27 Nov 2019 22:08:03 +0300 Subject: [PATCH 09/14] Update router entrypoint example --- docs/content/routing/providers/docker.md | 2 +- docs/content/routing/providers/marathon.md | 2 +- docs/content/routing/providers/rancher.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index f1f2938e1..a111a3d21 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -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. ```yaml - - "traefik.http.routers.myrouter.entrypoints=web,websecure" + - "traefik.http.routers.myrouter.entrypoints=ep1,ep2" ``` ??? info "`traefik.http.routers..middlewares`" diff --git a/docs/content/routing/providers/marathon.md b/docs/content/routing/providers/marathon.md index f8a50ac5f..b272b42a0 100644 --- a/docs/content/routing/providers/marathon.md +++ b/docs/content/routing/providers/marathon.md @@ -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. ```json - "traefik.http.routers.myrouter.entrypoints": "web,websecure" + "traefik.http.routers.myrouter.entrypoints": "ep1,ep2" ``` ??? info "`traefik.http.routers..middlewares`" diff --git a/docs/content/routing/providers/rancher.md b/docs/content/routing/providers/rancher.md index f8255b0ac..c0178a910 100644 --- a/docs/content/routing/providers/rancher.md +++ b/docs/content/routing/providers/rancher.md @@ -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. ```yaml - - "traefik.http.routers.myrouter.entrypoints=web,websecure" + - "traefik.http.routers.myrouter.entrypoints=ep1,ep2" ``` ??? info "`traefik.http.routers..middlewares`" From a87c1041727739854be13f16fea3f35f61e4ae57 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Thu, 28 Nov 2019 08:24:06 -0600 Subject: [PATCH 10/14] Remove Request Headers CORS Preflight Requirement --- pkg/middlewares/headers/headers.go | 6 ++---- pkg/middlewares/headers/headers_test.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go index 149b38219..0fce61796 100644 --- a/pkg/middlewares/headers/headers.go +++ b/pkg/middlewares/headers/headers.go @@ -221,13 +221,11 @@ func (s *Header) processCorsHeaders(rw http.ResponseWriter, req *http.Request) b } reqAcMethod := req.Header.Get("Access-Control-Request-Method") - reqAcHeaders := req.Header.Get("Access-Control-Request-Headers") 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, - // and Access-Control-Request-Headers headers, and Origin headers, - // then it is a CORS preflight request, + // and Origin headers, then it is a CORS preflight request, // and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request if s.headers.AccessControlAllowCredentials { rw.Header().Set("Access-Control-Allow-Credentials", "true") diff --git a/pkg/middlewares/headers/headers_test.go b/pkg/middlewares/headers/headers_test.go index 21df2deff..e335399dc 100644 --- a/pkg/middlewares/headers/headers_test.go +++ b/pkg/middlewares/headers/headers_test.go @@ -275,6 +275,25 @@ func TestCORSPreflights(t *testing.T) { "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 { From efcc9d51d4f9b20380a53614e435f343bbbdafba Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou Date: Fri, 29 Nov 2019 12:40:05 +0100 Subject: [PATCH 11/14] Healthcheck managed for all related services Co-authored-by: Mathieu Lonjaret --- .../multiple-routers-one-same-service.toml | 40 +++++++++++ integration/healthcheck_test.go | 66 +++++++++++++++++++ pkg/healthcheck/healthcheck.go | 63 ++++++++++++++---- pkg/server/service/service.go | 29 ++++---- 4 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 integration/fixtures/healthcheck/multiple-routers-one-same-service.toml diff --git a/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml b/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml new file mode 100644 index 000000000..a2d4d5147 --- /dev/null +++ b/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml @@ -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" diff --git a/integration/healthcheck_test.go b/integration/healthcheck_test.go index 999c69a9a..16d17a0ba 100644 --- a/integration/healthcheck_test.go +++ b/integration/healthcheck_test.go @@ -205,3 +205,69 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) { err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable)) 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) +} diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index c1385c399..95972474d 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -25,14 +25,20 @@ const ( var singleton *HealthCheck var once sync.Once -// BalancerHandler includes functionality for load-balancing management. -type BalancerHandler interface { - ServeHTTP(w http.ResponseWriter, req *http.Request) +// Balancer is the set of operations required to manage the list of servers in a +// load-balancer. +type Balancer interface { Servers() []*url.URL RemoveServer(u *url.URL) 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 // necessary for the health check package. This makes it easier for the tests. type metricsRegistry interface { @@ -49,7 +55,7 @@ type Options struct { Transport http.RoundTripper Interval time.Duration Timeout time.Duration - LB BalancerHandler + LB Balancer } func (opt Options) String() string { @@ -146,18 +152,18 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig) enabledURLs := backend.LB.Servers() var newDisabledURLs []backendURL // FIXME re enable metrics - for _, disableURL := range backend.disabledURLs { + for _, disabledURL := range backend.disabledURLs { // 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", - backend.name, disableURL.url.String(), disableURL.weight) - if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil { + backend.name, disabledURL.url.String(), disabledURL.weight) + if err = backend.LB.UpsertServer(disabledURL.url, roundrobin.Weight(disabledURL.weight)); err != nil { logger.Error(err) } // FIXME serverUpMetricValue = 1 } else { - logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disableURL.url.String(), err) - newDisabledURLs = append(newDisabledURLs, disableURL) + logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disabledURL.url.String(), err) + newDisabledURLs = append(newDisabledURLs, disabledURL) } // FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()} // FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue) @@ -177,7 +183,7 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig) 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 { logger.Error(err) } @@ -281,3 +287,38 @@ func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.Server } 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 +} diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 9c000b171..666e44ef9 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -40,7 +40,7 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt metricsRegistry: metricsRegistry, bufferPool: newBufferPool(), defaultRoundTripper: defaultRoundTripper, - balancers: make(map[string][]healthcheck.BalancerHandler), + balancers: make(map[string]healthcheck.Balancers), configs: configs, api: api, rest: rest, @@ -53,10 +53,14 @@ type Manager struct { metricsRegistry metrics.Registry bufferPool httputil.BufferPool defaultRoundTripper http.RoundTripper - balancers map[string][]healthcheck.BalancerHandler - configs map[string]*runtime.ServiceInfo - api http.Handler - rest http.Handler + // 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 + api http.Handler + rest http.Handler } // BuildHTTP Creates a http.Handler for a service configuration. @@ -110,14 +114,14 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons } case conf.Weighted != nil: var err error - lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier) + lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier) if err != nil { conf.AddError(err, true) return nil, err } case conf.Mirroring != nil: var err error - lb, err = m.getLoadBalancerMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier) + lb, err = m.getMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier) if err != nil { conf.AddError(err, true) return nil, err @@ -131,7 +135,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons 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, serviceName string, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) { serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier) if err != nil { return nil, err @@ -152,7 +156,7 @@ func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, servi 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 if config.Sticky != nil && config.Sticky.Cookie != nil { config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName) @@ -218,15 +222,12 @@ func (m *Manager) LaunchHealthCheck() { for serviceName, balancers := range m.balancers { 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 service := m.configs[serviceName].LoadBalancer // Health Check 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) hcOpts.Transport = m.defaultRoundTripper @@ -242,7 +243,7 @@ func (m *Manager) LaunchHealthCheck() { 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 == "" { return nil } From 2af8589afd0bb76e8e7b5de5f264e7ac74730e27 Mon Sep 17 00:00:00 2001 From: mpl Date: Mon, 2 Dec 2019 03:14:04 +0100 Subject: [PATCH 12/14] Do not give responsewriter or its headers to asynchronous logging goroutine Co-authored-by: Julien Salleyron --- pkg/middlewares/accesslog/logdata.go | 15 +++++++- pkg/middlewares/accesslog/logger.go | 47 ++++++++++++++---------- pkg/middlewares/accesslog/logger_test.go | 1 + 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go index c21e1c2f3..ae39ce9d6 100644 --- a/pkg/middlewares/accesslog/logdata.go +++ b/pkg/middlewares/accesslog/logdata.go @@ -116,7 +116,18 @@ type CoreLogData map[string]interface{} // LogData is the data captured by the middleware so that it can be logged. type LogData struct { Core CoreLogData - Request http.Header + Request request 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 } diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index 53bebac6e..5f9a3c9f9 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -47,8 +47,6 @@ func (n noopCloser) Close() error { type handlerParams struct { logDataTable *LogData - crr *captureRequestReader - crw *captureResponseWriter } // 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() { defer logHandler.wg.Done() 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(), } - 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)) @@ -205,16 +208,21 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http 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 { h.logHandlerChan <- handlerParams{ logDataTable: logDataTable, - crr: crr, - crw: crw, } } 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. -func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) { +func (h *Handler) logTheRoundTrip(logDataTable *LogData) { core := logDataTable.Core retryAttempts, ok := core[RetryAttempts].(int) @@ -272,23 +280,22 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead retryAttempts = 0 } core[RetryAttempts] = retryAttempts + core[RequestContentSize] = logDataTable.Request.count - if crr != nil { - core[RequestContentSize] = crr.count - } - - core[DownstreamStatus] = crw.Status() + status := logDataTable.DownstreamResponse.status + core[DownstreamStatus] = status // 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)) core[Duration] = totalDuration - if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) { - core[DownstreamContentSize] = crw.Size() + if h.keepAccessLog(status, retryAttempts, totalDuration) { + size := logDataTable.DownstreamResponse.size + core[DownstreamContentSize] = size if original, ok := core[OriginContentSize]; ok { o64 := original.(int64) - if crw.Size() != o64 && crw.Size() != 0 { - core[GzipRatio] = float64(o64) / float64(crw.Size()) + if size != o64 && size != 0 { + 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.DownstreamResponse, fields, "downstream_") + h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_") h.mu.Lock() defer h.mu.Unlock() diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index b93d8fdcd..ad81c1c0b 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -192,6 +192,7 @@ func TestLoggerJSON(t *testing.T) { Format: JSONFormat, }, expected: map[string]func(t *testing.T, value interface{}){ + RequestContentSize: assertFloat64(0), RequestHost: assertString(testHostname), RequestAddr: assertString(testHostname), RequestMethod: assertString(testMethod), From 78097b96c9c934b562dabd7c444a0bf6398c9747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Mon, 2 Dec 2019 14:18:08 +0100 Subject: [PATCH 13/14] Fix extraction for zipkin tracing --- go.mod | 2 +- go.sum | 4 ++-- pkg/middlewares/tracing/entrypoint.go | 6 +++++- pkg/tracing/carrier.go | 25 ------------------------- pkg/tracing/tracing.go | 2 +- 5 files changed, 9 insertions(+), 30 deletions(-) delete mode 100644 pkg/tracing/carrier.go diff --git a/go.mod b/go.mod index f19f21bb0..74ce6e412 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc8 // indirect github.com/opentracing/basictracer-go v1.0.0 // indirect 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/patrickmn/go-cache v2.1.0+incompatible github.com/philhofer/fwd v1.0.0 // indirect diff --git a/go.sum b/go.sum index 8a01a5fe3..0be6c4e8c 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,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/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= 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.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= +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.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= diff --git a/pkg/middlewares/tracing/entrypoint.go b/pkg/middlewares/tracing/entrypoint.go index 195cc994f..007a9c6e3 100644 --- a/pkg/middlewares/tracing/entrypoint.go +++ b/pkg/middlewares/tracing/entrypoint.go @@ -34,7 +34,11 @@ type entryPointMiddleware struct { } 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)) defer finish() diff --git a/pkg/tracing/carrier.go b/pkg/tracing/carrier.go deleted file mode 100644 index 57f54865d..000000000 --- a/pkg/tracing/carrier.go +++ /dev/null @@ -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 -} diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index 2d19aa7da..9eddc8933 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -134,7 +134,7 @@ func InjectRequestHeaders(r *http.Request) { err := opentracing.GlobalTracer().Inject( span.Context(), opentracing.HTTPHeaders, - HTTPHeadersCarrier(r.Header)) + opentracing.HTTPHeadersCarrier(r.Header)) if err != nil { log.FromContext(r.Context()).Error(err) } From ecd51a142866a788f3cce4cb633cc623e5a65bd6 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 2 Dec 2019 18:14:05 +0100 Subject: [PATCH 14/14] Prepare release v2.0.6 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cec1b7d4..5d362fdda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## [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.0.5](https://github.com/containous/traefik/tree/v2.0.5) (2019-11-14) [All Commits](https://github.com/containous/traefik/compare/v2.0.4...v2.0.5)