From 7357d5eae2d5bf4262b5352def76e4a652191a07 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 12 Sep 2019 10:18:04 +0200 Subject: [PATCH 01/19] fix: services configuration documentation. --- docs/content/middlewares/digestauth.md | 3 +- docs/content/routing/services/index.md | 38 +++++++++---------- pkg/middlewares/auth/basic_auth_test.go | 3 -- .../kubernetes/crd/kubernetes_test.go | 4 -- 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/docs/content/middlewares/digestauth.md b/docs/content/middlewares/digestauth.md index 0462599bd..c4d57c2da 100644 --- a/docs/content/middlewares/digestauth.md +++ b/docs/content/middlewares/digestauth.md @@ -97,8 +97,7 @@ metadata: data: users: |2 - dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5 - aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK + dGVzdDp0cmFlZmlrOmEyNjg4ZTAzMWVkYjRiZTZhMzc5N2YzODgyNjU1YzA1CnRlc3QyOnRyYWVmaWs6NTE4ODQ1ODAwZjllMmJmYjFmMWY3NDBlYzI0ZjA3NGUKCg== ``` ```json tab="Marathon" diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 7f0a8bf4a..12dcdeed5 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -312,11 +312,11 @@ This strategy can be defined only with [File](../../providers/file.md). ```toml tab="TOML" [http.services] - [http.services.canary] - [[http.services.canary.weighted.services]] + [http.services.app] + [[http.services.app.weighted.services]] name = "appv1" weight = 3 - [[http.services.canary.weighted.services]] + [[http.services.app.weighted.services]] name = "appv2" weight = 1 @@ -334,7 +334,7 @@ This strategy can be defined only with [File](../../providers/file.md). ```yaml tab="YAML" http: services: - canary: + app: weighted: services: - name: appv1 @@ -361,40 +361,40 @@ This strategy can be defined only with [File](../../providers/file.md). ```toml tab="TOML" [http.services] - [http.services.mirroring] - [http.services.mirroring.mirroring] - service = "app" - [[http.services.mirroring.mirroring.mirrors]] - name = "mirror" + [http.services.mirrored-api] + [http.services.mirrored-api.mirroring] + service = "appv1" + [[http.services.mirrored-api.mirroring.mirrors]] + name = "appv2" percent = 10 - [http.services.app] - [http.services.app.loadBalancer] + [http.services.appv1] + [http.services.appv1.loadBalancer] [[http.services.appv1.loadBalancer.servers]] url = "http://private-ip-server-1/" - [http.services.mirror] - [http.services.mirror.loadBalancer] - [[http.services.mirror.loadBalancer.servers]] + [http.services.appv2] + [http.services.appv2.loadBalancer] + [[http.services.appv2.loadBalancer.servers]] url = "http://private-ip-server-2/" ``` ```yaml tab="YAML" http: services: - mirroring: + mirrored-api: mirroring: - service: app + service: appv1 mirrors: - - name: mirror + - name: appv2 percent: 10 - app: + appv1: loadBalancer: servers: - url: "http://private-ip-server-1/" - mirror: + appv2: loadBalancer: servers: - url: "http://private-ip-server-2/" diff --git a/pkg/middlewares/auth/basic_auth_test.go b/pkg/middlewares/auth/basic_auth_test.go index 19e176202..f60fc4423 100644 --- a/pkg/middlewares/auth/basic_auth_test.go +++ b/pkg/middlewares/auth/basic_auth_test.go @@ -210,9 +210,6 @@ func TestBasicAuthUsersFromFile(t *testing.T) { } for _, test := range testCases { - if test.desc != "Should skip comments" { - continue - } test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 6dc17b5e0..e22f9e0f7 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1543,10 +1543,6 @@ func TestLoadIngressRoutes(t *testing.T) { for _, test := range testCases { test := test - - if test.desc != "Simple Ingress Route, with error page middleware" { - continue - } t.Run(test.desc, func(t *testing.T) { t.Parallel() From 887826ee68f8875d1453a3a725b4bdd1e5874842 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 12 Sep 2019 16:22:03 +0200 Subject: [PATCH 02/19] Improve documentation about API and Dashboard. --- docs/content/getting-started/quick-start.md | 4 ++-- docs/content/includes/.markdownlint.json | 4 ---- docs/content/includes/more-on-command-line.md | 1 - .../content/includes/more-on-configuration-file.md | 1 - docs/content/includes/more-on-entrypoints.md | 2 -- docs/content/includes/more-on-key-value-store.md | 1 - docs/content/includes/more-on-routers.md | 2 -- docs/content/operations/dashboard.md | 14 ++++++++------ .../user-guides/crd-acme/03-deployments.yml | 2 +- .../docker-compose/acme-dns/docker-compose.yml | 4 ++-- .../acme-dns/docker-compose_secrets.yml | 4 ++-- .../docker-compose/acme-http/docker-compose.yml | 4 ++-- .../docker-compose/acme-tls/docker-compose.yml | 4 ++-- .../basic-example/docker-compose.yml | 4 ++-- .../docker-compose/basic-example/index.md | 2 +- docs/content/user-guides/grpc.md | 4 ++-- docs/mkdocs.yml | 6 +++--- 17 files changed, 27 insertions(+), 36 deletions(-) delete mode 100644 docs/content/includes/.markdownlint.json delete mode 100644 docs/content/includes/more-on-command-line.md delete mode 100644 docs/content/includes/more-on-configuration-file.md delete mode 100644 docs/content/includes/more-on-entrypoints.md delete mode 100644 docs/content/includes/more-on-key-value-store.md delete mode 100644 docs/content/includes/more-on-routers.md diff --git a/docs/content/getting-started/quick-start.md b/docs/content/getting-started/quick-start.md index abe74af88..e629763ec 100644 --- a/docs/content/getting-started/quick-start.md +++ b/docs/content/getting-started/quick-start.md @@ -17,11 +17,11 @@ services: # The official v2.0 Traefik docker image image: traefik:v2.0 # Enables the web UI and tells Traefik to listen to docker - command: --api --providers.docker + command: --api.insecure=true --providers.docker ports: # The HTTP port - "80:80" - # The Web UI (enabled by --api) + # The Web UI (enabled by --api.insecure=true) - "8080:8080" volumes: # So that Traefik can listen to the Docker events diff --git a/docs/content/includes/.markdownlint.json b/docs/content/includes/.markdownlint.json deleted file mode 100644 index 3ad8a7f24..000000000 --- a/docs/content/includes/.markdownlint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../.markdownlint.json", - "MD041": false -} diff --git a/docs/content/includes/more-on-command-line.md b/docs/content/includes/more-on-command-line.md deleted file mode 100644 index c7ee03498..000000000 --- a/docs/content/includes/more-on-command-line.md +++ /dev/null @@ -1 +0,0 @@ -To learn more about configuration options in the command line, refer to the [configuration overview](../getting-started/configuration-overview.md) diff --git a/docs/content/includes/more-on-configuration-file.md b/docs/content/includes/more-on-configuration-file.md deleted file mode 100644 index 746c1608b..000000000 --- a/docs/content/includes/more-on-configuration-file.md +++ /dev/null @@ -1 +0,0 @@ -To learn more about the configuration file, refer to [configuration overview](../getting-started/configuration-overview.md) diff --git a/docs/content/includes/more-on-entrypoints.md b/docs/content/includes/more-on-entrypoints.md deleted file mode 100644 index 593b78ce8..000000000 --- a/docs/content/includes/more-on-entrypoints.md +++ /dev/null @@ -1,2 +0,0 @@ -!!! info "More On Entry Points" - Learn more about entry points and their configuration options in the dedicated section. \ No newline at end of file diff --git a/docs/content/includes/more-on-key-value-store.md b/docs/content/includes/more-on-key-value-store.md deleted file mode 100644 index 714952afc..000000000 --- a/docs/content/includes/more-on-key-value-store.md +++ /dev/null @@ -1 +0,0 @@ -To learn more about configuration in key-value stores, refer to the [configuration overview](../getting-started/configuration-overview.md) diff --git a/docs/content/includes/more-on-routers.md b/docs/content/includes/more-on-routers.md deleted file mode 100644 index 65bf7fbad..000000000 --- a/docs/content/includes/more-on-routers.md +++ /dev/null @@ -1,2 +0,0 @@ -!!! info "More On Routers" - Learn more about routers and their configuration options in the [dedicated section](../routing/routers/index.md). diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index a73a52ee2..d22356608 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -12,13 +12,13 @@ The dashboard is the central place that shows you the current active routes hand By default, the dashboard is available on `/` on port `:8080`. -!!! tip "Did You Know?" +!!! note "Did You Know?" It is possible to customize the dashboard endpoint. To learn how, refer to the [API documentation](./api.md) ## Enabling the Dashboard -To enable the dashboard, you need to enable Traefik's API. +To enable the dashboard, you need to enable [Traefik's API](./api.md). ```toml tab="File (TOML)" [api] @@ -49,10 +49,12 @@ api: --api.dashboard=true ``` -{!more-on-command-line.md!} +!!! important "API/Dashboard Security" + + To secure your dashboard, the use of a `service` named `api@internal` is mandatory and requires the definition of a router using one or more security [middlewares](../middlewares/overview.md) + like authentication ([basicAuth](../middlewares/basicauth.md) , [digestAuth](../middlewares/digestauth.md), [forwardAuth](../middlewares/forwardauth.md)) or [whitelisting](../middlewares/ipwhitelist.md). + More information about `api@internal` can be found in the [API documentation](./api.md#configuration) -{!more-on-configuration-file.md!} - -!!! tip "Did You Know?" +!!! note "Did You Know?" The API provides more features than the Dashboard. To learn more about it, refer to the [API documentation](./api.md) diff --git a/docs/content/user-guides/crd-acme/03-deployments.yml b/docs/content/user-guides/crd-acme/03-deployments.yml index 71780e9ce..1d1f98c3a 100644 --- a/docs/content/user-guides/crd-acme/03-deployments.yml +++ b/docs/content/user-guides/crd-acme/03-deployments.yml @@ -28,7 +28,7 @@ spec: - name: traefik image: traefik:v2.0 args: - - --api + - --api.insecure - --accesslog - --entrypoints.web.Address=:8000 - --entrypoints.websecure.Address=:4443 diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml index bfba9fe4c..14cf5c435 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml @@ -3,11 +3,11 @@ version: "3.3" services: traefik: - image: "traefik:v2.0.0-beta1" + image: "traefik:v2.0.0-rc3" container_name: "traefik" command: #- "--log.level=DEBUG" - - "--api=true" + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml index 7f41f26de..03d00179d 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml @@ -13,11 +13,11 @@ secrets: services: traefik: - image: "traefik:v2.0.0-beta1" + image: "traefik:v2.0.0-rc3" container_name: "traefik" command: #- "--log.level=DEBUG" - - "--api=true" + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" diff --git a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml index 8f348a7a8..b63906d55 100644 --- a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml @@ -3,11 +3,11 @@ version: "3.3" services: traefik: - image: "traefik:v2.0.0-beta1" + image: "traefik:v2.0.0-rc3" container_name: "traefik" command: #- "--log.level=DEBUG" - - "--api=true" + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" diff --git a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml index 1b03ce8d3..fe1ae9f1f 100644 --- a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml @@ -3,11 +3,11 @@ version: "3.3" services: traefik: - image: "traefik:v2.0.0-beta1" + image: "traefik:v2.0.0-rc3" container_name: "traefik" command: #- "--log.level=DEBUG" - - "--api=true" + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.websecure.address=:443" diff --git a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml index 5fb8bf9dd..327ab5b67 100644 --- a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml @@ -3,11 +3,11 @@ version: "3.3" services: traefik: - image: "traefik:v2.0.0-beta1" + image: "traefik:v2.0.0-rc3" container_name: "traefik" command: #- "--log.level=DEBUG" - - "--api=true" + - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" diff --git a/docs/content/user-guides/docker-compose/basic-example/index.md b/docs/content/user-guides/docker-compose/basic-example/index.md index 85d8fb495..55f9062aa 100644 --- a/docs/content/user-guides/docker-compose/basic-example/index.md +++ b/docs/content/user-guides/docker-compose/basic-example/index.md @@ -53,7 +53,7 @@ ports: ```yaml command: # Traefik will listen on port 8080 by default for API request. - - "--api=true" + - "--api.insecure=true" ports: - "8080:8080" diff --git a/docs/content/user-guides/grpc.md b/docs/content/user-guides/grpc.md index 6748445df..bbc70673f 100644 --- a/docs/content/user-guides/grpc.md +++ b/docs/content/user-guides/grpc.md @@ -34,7 +34,7 @@ api: {} ```yaml tab="CLI" --entryPoints.web.address=":80" --providers.file.filename=dynamic_conf.toml ---api=true +--api.insecure=true ``` `dynamic_conf.{toml,yml}`: @@ -157,7 +157,7 @@ api: {} # For secure connection on backend.local --serversTransport.rootCAs=./backend.cert --providers.file.filename=dynamic_conf.toml ---api=true +--api.insecure=true ``` `dynamic_conf.{toml,yml}`: diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index b7f09c0e1..6af5e4126 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -58,9 +58,9 @@ markdown_extensions: - pymdownx.tasklist - pymdownx.snippets: check_paths: true - - markdown_include.include: - base_path: content/includes/ - encoding: utf-8 +# - markdown_include.include: +# base_path: content/includes/ +# encoding: utf-8 - toc: permalink: true From 149ed91afb364ab4fc7847b9f716ff3a884e4338 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 13 Sep 2019 08:24:04 +0200 Subject: [PATCH 03/19] fix: passHostHeader in the webUI. --- webui/src/components/_commons/PanelServiceDetails.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webui/src/components/_commons/PanelServiceDetails.vue b/webui/src/components/_commons/PanelServiceDetails.vue index f173c9b69..7acfe1045 100644 --- a/webui/src/components/_commons/PanelServiceDetails.vue +++ b/webui/src/components/_commons/PanelServiceDetails.vue @@ -33,9 +33,9 @@ - +
-
+
Main Service
- +
-
+
Pass Host Header
- +
From 7fa35370155be292c732288b96ec25f409ccedb9 Mon Sep 17 00:00:00 2001 From: Denis Zavershinskiy Date: Fri, 13 Sep 2019 16:30:05 +0700 Subject: [PATCH 04/19] fix a service with one server .yaml example --- docs/content/routing/services/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 12dcdeed5..f452a6827 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -105,7 +105,7 @@ The `url` option point to a specific instance. my-service: loadBalancer: servers: - url: "http://private-ip-server-1/" + - url: "http://private-ip-server-1/" ``` #### Load-balancing From 401b3afa3b75b996bfa19b1d0a6a1d4a4c6c673f Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Fri, 13 Sep 2019 16:46:04 +0200 Subject: [PATCH 05/19] Add passHostHeader and responseForwarding in IngressRoute Co-authored-by: Mathieu Lonjaret --- docs/content/providers/kubernetes-crd.md | 7 ++++ .../ratelimiter/rate_limiter_test.go | 19 ++++----- .../kubernetes/crd/fixtures/with_options.yml | 20 ++++++++++ .../kubernetes/crd/kubernetes_http.go | 16 +++++--- .../kubernetes/crd/kubernetes_test.go | 39 +++++++++++++++++++ .../crd/traefik/v1alpha1/ingressroute.go | 15 ++++--- .../traefik/v1alpha1/zz_generated.deepcopy.go | 10 +++++ 7 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 pkg/provider/kubernetes/crd/fixtures/with_options.yml diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index 653584187..24c95b4e7 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -226,6 +226,13 @@ spec: port: 80 # (default 1) A weight used by the weighted round-robin strategy (WRR). weight: 1 + # (default true) PassHostHeader controls whether to leave the request's Host + # Header as it was before it reached the proxy, or whether to let the proxy set it + # to the destination (backend) host. + passHostHeader: true + responseForwarding: + # (default 100ms) Interval between flushes of the buffered response body to the client. + flushInterval: 100ms --- apiVersion: traefik.containo.us/v1alpha1 diff --git a/pkg/middlewares/ratelimiter/rate_limiter_test.go b/pkg/middlewares/ratelimiter/rate_limiter_test.go index 6273ca356..ad4474870 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter_test.go +++ b/pkg/middlewares/ratelimiter/rate_limiter_test.go @@ -127,15 +127,16 @@ func TestRateLimit(t *testing.T) { incomingLoad: 200, burst: 300, }, - { - desc: "Zero average ==> no rate limiting", - config: dynamic.RateLimit{ - Average: 0, - Burst: 1, - }, - incomingLoad: 1000, - loadDuration: time.Second, - }, + // TODO Try to disambiguate when it fails if it is because of too high a load. + // { + // desc: "Zero average ==> no rate limiting", + // config: dynamic.RateLimit{ + // Average: 0, + // Burst: 1, + // }, + // incomingLoad: 1000, + // loadDuration: time.Second, + // }, } for _, test := range testCases { diff --git a/pkg/provider/kubernetes/crd/fixtures/with_options.yml b/pkg/provider/kubernetes/crd/fixtures/with_options.yml new file mode 100644 index 000000000..1c945829d --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_options.yml @@ -0,0 +1,20 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: Host(`foo.com`) && PathPrefix(`/bar`) + kind: Rule + priority: 12 + services: + - name: whoami + port: 80 + passHostHeader: false + responseForwarding: + flushInterval: 10s diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index bbadee6a4..5144d330f 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -157,12 +157,18 @@ func createLoadBalancerServerHTTP(client Client, namespace string, service v1alp return nil, err } + // TODO: support other strategies. + lb := &dynamic.ServersLoadBalancer{} + lb.SetDefaults() + + lb.Servers = servers + if service.PassHostHeader != nil { + lb.PassHostHeader = *service.PassHostHeader + } + lb.ResponseForwarding = service.ResponseForwarding + return &dynamic.Service{ - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: servers, - // TODO: support other strategies. - PassHostHeader: true, - }, + LoadBalancer: lb, }, nil } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index e22f9e0f7..03ceb3de2 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1536,6 +1536,44 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + { + desc: "Simple Ingress Route, with options", + paths: []string{"services.yml", "with_options.yml"}, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default/test.route-6b204d94623b3df4370c": { + EntryPoints: []string{"foo"}, + Service: "default/test.route-6b204d94623b3df4370c", + Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", + Priority: 12, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default/test.route-6b204d94623b3df4370c": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + PassHostHeader: false, + ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: "10s"}, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, { desc: "port selected by name (TODO)", }, @@ -1543,6 +1581,7 @@ func TestLoadIngressRoutes(t *testing.T) { for _, test := range testCases { test := test + t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go index 6a1a56782..a1f5d325c 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -46,12 +47,14 @@ type TLSOptionRef struct { // Service defines an upstream to proxy traffic. type Service struct { - Name string `json:"name"` - Port int32 `json:"port"` - Scheme string `json:"scheme,omitempty"` - HealthCheck *HealthCheck `json:"healthCheck,omitempty"` - Strategy string `json:"strategy,omitempty"` - Weight *int `json:"weight,omitempty"` + Name string `json:"name"` + Port int32 `json:"port"` + Scheme string `json:"scheme,omitempty"` + HealthCheck *HealthCheck `json:"healthCheck,omitempty"` + Strategy string `json:"strategy,omitempty"` + PassHostHeader *bool `json:"passHostHeader,omitempty"` + ResponseForwarding *dynamic.ResponseForwarding `json:"responseForwarding,omitempty"` + Weight *int `json:"weight,omitempty"` } // MiddlewareRef is a ref to the Middleware resources. diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go index 813b8a6b5..85d073da4 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go @@ -635,6 +635,16 @@ func (in *Service) DeepCopyInto(out *Service) { *out = new(HealthCheck) (*in).DeepCopyInto(*out) } + if in.PassHostHeader != nil { + in, out := &in.PassHostHeader, &out.PassHostHeader + *out = new(bool) + **out = **in + } + if in.ResponseForwarding != nil { + in, out := &in.ResponseForwarding, &out.ResponseForwarding + *out = new(dynamic.ResponseForwarding) + **out = **in + } if in.Weight != nil { in, out := &in.Weight, &out.Weight *out = new(int) From b55be9fdea3ad4e00a94edd93686e073f19f3935 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Fri, 13 Sep 2019 17:46:04 +0200 Subject: [PATCH 06/19] On client CloseWrite, do CloseWrite instead of Close for backend Co-authored-by: Mathieu Lonjaret --- .../dynamic-configuration/docker-labels.yml | 2 + .../reference/dynamic-configuration/file.toml | 3 + .../reference/dynamic-configuration/file.yaml | 2 + .../marathon-labels.json | 2 + docs/content/routing/services/index.md | 31 +++++++ integration/testdata/rawdata-crd.json | 1 + pkg/config/dynamic/tcp_config.go | 14 +++- pkg/config/dynamic/zz_generated.deepcopy.go | 5 ++ pkg/config/label/label_test.go | 4 + pkg/provider/docker/config.go | 1 + pkg/provider/docker/config_test.go | 53 ++++++++++++ pkg/provider/marathon/config.go | 1 + pkg/provider/marathon/config_test.go | 47 +++++++++++ pkg/provider/rancher/config.go | 1 + pkg/provider/rancher/config_test.go | 45 +++++++++++ pkg/server/server_entrypoint_tcp.go | 50 ++++++++++-- pkg/server/server_entrypoint_tcp_test.go | 2 +- pkg/server/service/tcp/service.go | 9 ++- pkg/tcp/handler.go | 15 +++- pkg/tcp/proxy.go | 38 ++++++--- pkg/tcp/proxy_test.go | 81 +++++++++++++++++++ pkg/tcp/router.go | 12 +-- pkg/tcp/rr_load_balancer.go | 3 +- pkg/tcp/switcher.go | 4 +- pkg/tcp/tls.go | 3 +- 25 files changed, 393 insertions(+), 36 deletions(-) create mode 100644 pkg/tcp/proxy_test.go diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 757e525cf..722a4fdf4 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -184,4 +184,6 @@ - "traefik.tcp.routers.tcprouter1.tls.options=foobar" - "traefik.tcp.routers.tcprouter1.tls.passthrough=true" - "traefik.tcp.services.tcpservice0.loadbalancer.server.port=foobar" +- "traefik.tcp.services.tcpservice0.loadbalancer.terminationdelay=100" - "traefik.tcp.services.tcpservice1.loadbalancer.server.port=foobar" +- "traefik.tcp.services.tcpservice1.loadbalancer.terminationdelay=100" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 88fc62144..8027a082b 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -286,14 +286,17 @@ [tcp.services] [tcp.services.TCPService0] [tcp.services.TCPService0.loadBalancer] + terminationDelay = 100 [[tcp.services.TCPService0.loadBalancer.servers]] address = "foobar" [[tcp.services.TCPService0.loadBalancer.servers]] address = "foobar" + [tcp.services.TCPService1] [tcp.services.TCPService1.loadBalancer] + terminationDelay = 100 [[tcp.services.TCPService1.loadBalancer.servers]] address = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 46a54373f..3c2eb98a4 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -324,11 +324,13 @@ tcp: services: TCPService0: loadBalancer: + terminationDelay: 100 servers: - address: foobar - address: foobar TCPService1: loadBalancer: + terminationDelay: 100 servers: - address: foobar - address: foobar diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index 5b1360841..695e8c6bd 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -184,4 +184,6 @@ "traefik.tcp.routers.tcprouter1.tls.options": "foobar", "traefik.tcp.routers.tcprouter1.tls.passthrough": "true", "traefik.tcp.services.tcpservice0.loadbalancer.server.port": "foobar", +"traefik.tcp.services.tcpservice0.loadbalancer.terminationDelay": "100", "traefik.tcp.services.tcpservice1.loadbalancer.server.port": "foobar" +"traefik.tcp.services.tcpservice1.loadbalancer.terminationDelay": "100", diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index f452a6827..0e9e109df 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -455,3 +455,34 @@ The `address` option (IP:Port) point to a specific instance. servers: address: "xx.xx.xx.xx:xx" ``` + +#### Termination Delay + +As a proxy between a client and a server, it can happen that either side (e.g. client side) decides to terminate its writing capability on the connection (i.e. issuance of a FIN packet). +The proxy needs to propagate that intent to the other side, and so when that happens, it also does the same on its connection with the other side (e.g. backend side). + +However, if for some reason (bad implementation, or malicious intent) the other side does not eventually do the same as well, +the connection would stay half-open, which would lock resources for however long. + +To that end, as soon as the proxy enters this termination sequence, it sets a deadline on fully terminating the connections on both sides. + +The termination delay controls that deadline. +It is a duration in milliseconds, defaulting to 100. +A negative value means an infinite deadline (i.e. the connection is never fully terminated by the proxy itself). + +??? example "A Service with a termination delay -- Using the [File Provider](../../providers/file.md)" + + ```toml tab="TOML" + [tcp.services] + [tcp.services.my-service.loadBalancer] + [[tcp.services.my-service.loadBalancer]] + terminationDelay = 200 + ``` + + ```yaml tab="YAML" + tcp: + services: + my-service: + loadBalancer: + terminationDelay: 200 + ``` diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 2cd67af47..98ccb952e 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -115,6 +115,7 @@ "tcpServices": { "default/test3.route-673acf455cb2dab0b43a@kubernetescrd": { "loadBalancer": { + "terminationDelay": 100, "servers": [ { "address": "10.42.0.4:8080" diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index e3e42ab68..d8ff19cb6 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -45,7 +45,19 @@ type RouterTCPTLSConfig struct { // TCPLoadBalancerService holds the LoadBalancerService configuration. type TCPLoadBalancerService struct { - Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` + // TerminationDelay, corresponds to the deadline that the proxy sets, after one + // of its connected peers indicates it has closed the writing capability of its + // connection, to close the reading capability as well, hence fully terminating the + // connection. It is a duration in milliseconds, defaulting to 100. A negative value + // means an infinite deadline (i.e. the reading capability is never closed). + TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty"` + Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` +} + +// SetDefaults Default values for a TCPLoadBalancerService +func (l *TCPLoadBalancerService) SetDefaults() { + defaultTerminationDelay := 100 // in milliseconds + l.TerminationDelay = &defaultTerminationDelay } // Mergeable tells if the given service is mergeable. diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index fa391b000..25d9d4807 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -1155,6 +1155,11 @@ func (in *TCPConfiguration) DeepCopy() *TCPConfiguration { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPLoadBalancerService) DeepCopyInto(out *TCPLoadBalancerService) { *out = *in + if in.TerminationDelay != nil { + in, out := &in.TerminationDelay, &out.TerminationDelay + *out = new(int) + **out = **in + } if in.Servers != nil { in, out := &in.Servers, &out.Servers *out = make([]TCPServer, len(*in)) diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 1fedf3046..989eb6be6 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -170,7 +170,9 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.tcp.routers.Router1.tls.options": "foo", "traefik.tcp.routers.Router1.tls.passthrough": "false", "traefik.tcp.services.Service0.loadbalancer.server.Port": "42", + "traefik.tcp.services.Service0.loadbalancer.TerminationDelay": "42", "traefik.tcp.services.Service1.loadbalancer.server.Port": "42", + "traefik.tcp.services.Service1.loadbalancer.TerminationDelay": "42", } configuration, err := DecodeConfiguration(labels) @@ -212,6 +214,7 @@ func TestDecodeConfiguration(t *testing.T) { Port: "42", }, }, + TerminationDelay: func(i int) *int { return &i }(42), }, }, "Service1": { @@ -221,6 +224,7 @@ func TestDecodeConfiguration(t *testing.T) { Port: "42", }, }, + TerminationDelay: func(i int) *int { return &i }(42), }, }, }, diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index aa5aaf376..86f097665 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -79,6 +79,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d if len(configuration.Services) == 0 { configuration.Services = make(map[string]*dynamic.TCPService) lb := &dynamic.TCPLoadBalancerService{} + lb.SetDefaults() configuration.Services[serviceName] = &dynamic.TCPService{ LoadBalancer: lb, } diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index c7c9bbdb5..2d927c0d7 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" ) +func Int(v int) *int { return &v } + func TestDefaultRule(t *testing.T) { testCases := []struct { desc string @@ -2092,6 +2094,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -2136,6 +2139,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -2190,6 +2194,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:8080", }, }, + TerminationDelay: Int(100), }, }, }, @@ -2268,6 +2273,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.2:8080", }, }, + TerminationDelay: Int(100), }, }, }, @@ -2331,6 +2337,53 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:8080", }, }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label for tcp service, with termination delay", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.services.foo.loadbalancer.server.port": "8080", + "traefik.tcp.services.foo.loadbalancer.terminationdelay": "200", + }, + NetworkSettings: networkSettings{ + Ports: nat.PortMap{ + nat.Port("80/tcp"): []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPLoadBalancerService{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:8080", + }, + }, + TerminationDelay: Int(200), }, }, }, diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go index 3f9adfebe..08a767af4 100644 --- a/pkg/provider/marathon/config.go +++ b/pkg/provider/marathon/config.go @@ -141,6 +141,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho if len(conf.Services) == 0 { conf.Services = make(map[string]*dynamic.TCPService) lb := &dynamic.TCPLoadBalancerService{} + lb.SetDefaults() conf.Services[appName] = &dynamic.TCPService{ LoadBalancer: lb, } diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go index 296490f08..c2dc92b30 100644 --- a/pkg/provider/marathon/config_test.go +++ b/pkg/provider/marathon/config_test.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/require" ) +func Int(v int) *int { return &v } + func TestGetConfigurationAPIErrors(t *testing.T) { fakeClient := newFakeClient(true, marathon.Applications{}) @@ -1240,6 +1242,7 @@ func TestBuildConfiguration(t *testing.T) { Address: "localhost:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -1271,6 +1274,7 @@ func TestBuildConfiguration(t *testing.T) { Address: "localhost:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -1310,6 +1314,48 @@ func TestBuildConfiguration(t *testing.T) { Address: "localhost:8080", }, }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one app with tcp labels with port, with termination delay", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + withLabel("traefik.tcp.routers.foo.rule", "HostSNI(`foo.bar`)"), + withLabel("traefik.tcp.routers.foo.tls", "true"), + withLabel("traefik.tcp.services.foo.loadbalancer.server.port", "8080"), + withLabel("traefik.tcp.services.foo.loadbalancer.terminationdelay", "200"), + )), + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": { + Service: "foo", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPLoadBalancerService{ + Servers: []dynamic.TCPServer{ + { + Address: "localhost:8080", + }, + }, + TerminationDelay: Int(200), }, }, }, @@ -1350,6 +1396,7 @@ func TestBuildConfiguration(t *testing.T) { Address: "localhost:8080", }, }, + TerminationDelay: Int(100), }, }, }, diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go index 4d6f63a13..e2787d96d 100644 --- a/pkg/provider/rancher/config.go +++ b/pkg/provider/rancher/config.go @@ -75,6 +75,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, service ran if len(configuration.Services) == 0 { configuration.Services = make(map[string]*dynamic.TCPService) lb := &dynamic.TCPLoadBalancerService{} + lb.SetDefaults() configuration.Services[serviceName] = &dynamic.TCPService{ LoadBalancer: lb, } diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go index 31fb29ddc..14d172f35 100644 --- a/pkg/provider/rancher/config_test.go +++ b/pkg/provider/rancher/config_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" ) +func Int(v int) *int { return &v } + func Test_buildConfiguration(t *testing.T) { testCases := []struct { desc string @@ -512,6 +514,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -548,6 +551,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:80", }, }, + TerminationDelay: Int(100), }, }, }, @@ -590,6 +594,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:8080", }, }, + TerminationDelay: Int(100), }, }, }, @@ -638,6 +643,7 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.2:8080", }, }, + TerminationDelay: Int(100), }, }, }, @@ -693,6 +699,45 @@ func Test_buildConfiguration(t *testing.T) { Address: "127.0.0.1:8080", }, }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label for tcp service, with termination delay", + containers: []rancherData{ + { + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.services.foo.loadbalancer.server.port": "8080", + "traefik.tcp.services.foo.loadbalancer.terminationdelay": "200", + }, + Port: "80/tcp", + Containers: []string{"127.0.0.1"}, + Health: "", + State: "", + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPLoadBalancerService{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:8080", + }, + }, + TerminationDelay: Int(200), }, }, }, diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index c3c4c43b8..cad56b3ab 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -37,7 +37,7 @@ func newHTTPForwarder(ln net.Listener) *httpForwarder { } // ServeTCP uses the connection to serve it later in "Accept" -func (h *httpForwarder) ServeTCP(conn net.Conn) { +func (h *httpForwarder) ServeTCP(conn tcp.WriteCloser) { h.connChan <- conn } @@ -99,7 +99,36 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint) (*T }, nil } +// writeCloserWrapper wraps together a connection, and the concrete underlying +// connection type that was found to satisfy WriteCloser. +type writeCloserWrapper struct { + net.Conn + writeCloser tcp.WriteCloser +} + +func (c *writeCloserWrapper) CloseWrite() error { + return c.writeCloser.CloseWrite() +} + +// writeCloser returns the given connection, augmented with the WriteCloser +// implementation, if any was found within the underlying conn. +func writeCloser(conn net.Conn) (tcp.WriteCloser, error) { + switch typedConn := conn.(type) { + case *proxyprotocol.Conn: + underlying, err := writeCloser(typedConn.Conn) + if err != nil { + return nil, err + } + return &writeCloserWrapper{writeCloser: underlying, Conn: typedConn}, nil + case *net.TCPConn: + return typedConn, nil + default: + return nil, fmt.Errorf("unknown connection type %T", typedConn) + } +} + func (e *TCPEntryPoint) startTCP(ctx context.Context) { + log.FromContext(ctx).Debugf("Start TCP Server") for { @@ -109,8 +138,13 @@ func (e *TCPEntryPoint) startTCP(ctx context.Context) { return } + writeCloser, err := writeCloser(conn) + if err != nil { + panic(err) + } + safe.Go(func() { - e.switcher.ServeTCP(newTrackedConnection(conn, e.tracker)) + e.switcher.ServeTCP(newTrackedConnection(writeCloser, e.tracker)) }) } } @@ -374,20 +408,20 @@ func createHTTPServer(ln net.Listener, configuration *static.EntryPoint, withH2c }, nil } -func newTrackedConnection(conn net.Conn, tracker *connectionTracker) *trackedConnection { +func newTrackedConnection(conn tcp.WriteCloser, tracker *connectionTracker) *trackedConnection { tracker.AddConnection(conn) return &trackedConnection{ - Conn: conn, - tracker: tracker, + WriteCloser: conn, + tracker: tracker, } } type trackedConnection struct { tracker *connectionTracker - net.Conn + tcp.WriteCloser } func (t *trackedConnection) Close() error { - t.tracker.RemoveConnection(t.Conn) - return t.Conn.Close() + t.tracker.RemoveConnection(t.WriteCloser) + return t.WriteCloser.Close() } diff --git a/pkg/server/server_entrypoint_tcp_test.go b/pkg/server/server_entrypoint_tcp_test.go index 525cea4f9..6bdce2a55 100644 --- a/pkg/server/server_entrypoint_tcp_test.go +++ b/pkg/server/server_entrypoint_tcp_test.go @@ -113,7 +113,7 @@ func TestShutdownTCPConn(t *testing.T) { go entryPoint.startTCP(context.Background()) router := &tcp.Router{} - router.AddCatchAllNoTLS(tcp.HandlerFunc(func(conn net.Conn) { + router.AddCatchAllNoTLS(tcp.HandlerFunc(func(conn tcp.WriteCloser) { _, err := http.ReadRequest(bufio.NewReader(conn)) require.NoError(t, err) time.Sleep(1 * time.Second) diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go index e5311a7eb..2495e2551 100644 --- a/pkg/server/service/tcp/service.go +++ b/pkg/server/service/tcp/service.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "time" "github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/log" @@ -44,13 +45,19 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han loadBalancer := tcp.NewRRLoadBalancer() + if conf.LoadBalancer.TerminationDelay == nil { + defaultTerminationDelay := 100 + conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay + } + duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay) + for name, server := range conf.LoadBalancer.Servers { if _, _, err := net.SplitHostPort(server.Address); err != nil { logger.Errorf("In service %q: %v", serviceQualifiedName, err) continue } - handler, err := tcp.NewProxy(server.Address) + handler, err := tcp.NewProxy(server.Address, duration) if err != nil { logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err) continue diff --git a/pkg/tcp/handler.go b/pkg/tcp/handler.go index 63158baee..88aefe8de 100644 --- a/pkg/tcp/handler.go +++ b/pkg/tcp/handler.go @@ -6,14 +6,23 @@ import ( // Handler is the TCP Handlers interface type Handler interface { - ServeTCP(conn net.Conn) + ServeTCP(conn WriteCloser) } // The HandlerFunc type is an adapter to allow the use of // ordinary functions as handlers. -type HandlerFunc func(conn net.Conn) +type HandlerFunc func(conn WriteCloser) // ServeTCP serves tcp -func (f HandlerFunc) ServeTCP(conn net.Conn) { +func (f HandlerFunc) ServeTCP(conn WriteCloser) { f(conn) } + +// WriteCloser describes a net.Conn with a CloseWrite method. +type WriteCloser interface { + net.Conn + // CloseWrite on a network connection, indicates that the issuer of the call + // has terminated sending on that connection. + // It corresponds to sending a FIN packet. + CloseWrite() error +} diff --git a/pkg/tcp/proxy.go b/pkg/tcp/proxy.go index 76a20aa91..2e7934415 100644 --- a/pkg/tcp/proxy.go +++ b/pkg/tcp/proxy.go @@ -3,28 +3,32 @@ package tcp import ( "io" "net" + "time" "github.com/containous/traefik/v2/pkg/log" ) // Proxy forwards a TCP request to a TCP service type Proxy struct { - target *net.TCPAddr + target *net.TCPAddr + terminationDelay time.Duration } // NewProxy creates a new Proxy -func NewProxy(address string) (*Proxy, error) { +func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", address) if err != nil { return nil, err } - return &Proxy{target: tcpAddr}, nil + return &Proxy{target: tcpAddr, terminationDelay: terminationDelay}, nil } // ServeTCP forwards the connection to a service -func (p *Proxy) ServeTCP(conn net.Conn) { +func (p *Proxy) ServeTCP(conn WriteCloser) { log.Debugf("Handling connection from %s", conn.RemoteAddr()) + + // needed because of e.g. server.trackedConnection defer conn.Close() connBackend, err := net.DialTCP("tcp", nil, p.target) @@ -32,19 +36,35 @@ func (p *Proxy) ServeTCP(conn net.Conn) { log.Errorf("Error while connection to backend: %v", err) return } + + // maybe not needed, but just in case defer connBackend.Close() - errChan := make(chan error, 1) - go connCopy(conn, connBackend, errChan) - go connCopy(connBackend, conn, errChan) + errChan := make(chan error) + go p.connCopy(conn, connBackend, errChan) + go p.connCopy(connBackend, conn, errChan) err = <-errChan if err != nil { - log.Errorf("Error during connection: %v", err) + log.WithoutContext().Errorf("Error during connection: %v", err) } + + <-errChan } -func connCopy(dst, src net.Conn, errCh chan error) { +func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) { _, err := io.Copy(dst, src) errCh <- err + + errClose := dst.CloseWrite() + if errClose != nil { + log.WithoutContext().Errorf("Error while terminating connection: %v", errClose) + } + + if p.terminationDelay >= 0 { + err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay)) + if err != nil { + log.WithoutContext().Errorf("Error while setting deadline: %v", err) + } + } } diff --git a/pkg/tcp/proxy_test.go b/pkg/tcp/proxy_test.go new file mode 100644 index 000000000..323a395ff --- /dev/null +++ b/pkg/tcp/proxy_test.go @@ -0,0 +1,81 @@ +package tcp + +import ( + "bytes" + "fmt" + "io" + "net" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func fakeRedis(t *testing.T, listener net.Listener) { + for { + conn, err := listener.Accept() + fmt.Println("Accept on server") + require.NoError(t, err) + for { + withErr := false + buf := make([]byte, 64) + if _, err := conn.Read(buf); err != nil { + withErr = true + } + + if string(buf[:4]) == "ping" { + time.Sleep(time.Millisecond * 1) + if _, err := conn.Write([]byte("PONG")); err != nil { + conn.Close() + return + } + } + if withErr { + conn.Close() + return + } + } + } +} + +func TestCloseWrite(t *testing.T) { + backendListener, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go fakeRedis(t, backendListener) + _, port, err := net.SplitHostPort(backendListener.Addr().String()) + require.NoError(t, err) + + proxy, err := NewProxy(":"+port, 10*time.Millisecond) + require.NoError(t, err) + + proxyListener, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go func() { + for { + conn, err := proxyListener.Accept() + require.NoError(t, err) + proxy.ServeTCP(conn.(*net.TCPConn)) + } + }() + + _, port, err = net.SplitHostPort(proxyListener.Addr().String()) + require.NoError(t, err) + + conn, err := net.Dial("tcp", ":"+port) + require.NoError(t, err) + + _, err = conn.Write([]byte("ping\n")) + require.NoError(t, err) + + err = conn.(*net.TCPConn).CloseWrite() + require.NoError(t, err) + + var buf []byte + buffer := bytes.NewBuffer(buf) + n, err := io.Copy(buffer, conn) + require.NoError(t, err) + require.Equal(t, int64(4), n) + require.Equal(t, "PONG", buffer.String()) +} diff --git a/pkg/tcp/router.go b/pkg/tcp/router.go index 7dc379373..f8a7348b0 100644 --- a/pkg/tcp/router.go +++ b/pkg/tcp/router.go @@ -25,7 +25,7 @@ type Router struct { } // ServeTCP forwards the connection to the right TCP/HTTP handler -func (r *Router) ServeTCP(conn net.Conn) { +func (r *Router) ServeTCP(conn WriteCloser) { // FIXME -- Check if ProxyProtocol changes the first bytes of the request if r.catchAllNoTLS != nil && len(r.routingTable) == 0 && r.httpsHandler == nil { @@ -99,11 +99,11 @@ func (r *Router) AddCatchAllNoTLS(handler Handler) { } // GetConn creates a connection proxy with a peeked string -func (r *Router) GetConn(conn net.Conn, peeked string) net.Conn { +func (r *Router) GetConn(conn WriteCloser, peeked string) WriteCloser { // FIXME should it really be on Router ? conn = &Conn{ - Peeked: []byte(peeked), - Conn: conn, + Peeked: []byte(peeked), + WriteCloser: conn, } return conn } @@ -157,7 +157,7 @@ type Conn struct { // It can be type asserted against *net.TCPConn or other types // as needed. It should not be read from directly unless // Peeked is nil. - net.Conn + WriteCloser } // Read reads bytes from the connection (using the buffer prior to actually reading) @@ -170,7 +170,7 @@ func (c *Conn) Read(p []byte) (n int, err error) { } return n, nil } - return c.Conn.Read(p) + return c.WriteCloser.Read(p) } // clientHelloServerName returns the SNI server name inside the TLS ClientHello, diff --git a/pkg/tcp/rr_load_balancer.go b/pkg/tcp/rr_load_balancer.go index 6187d5417..92be94f54 100644 --- a/pkg/tcp/rr_load_balancer.go +++ b/pkg/tcp/rr_load_balancer.go @@ -1,7 +1,6 @@ package tcp import ( - "net" "sync" "github.com/containous/traefik/v2/pkg/log" @@ -20,7 +19,7 @@ func NewRRLoadBalancer() *RRLoadBalancer { } // ServeTCP forwards the connection to the right service -func (r *RRLoadBalancer) ServeTCP(conn net.Conn) { +func (r *RRLoadBalancer) ServeTCP(conn WriteCloser) { if len(r.servers) == 0 { log.WithoutContext().Error("no available server") return diff --git a/pkg/tcp/switcher.go b/pkg/tcp/switcher.go index 330fc993f..16c4c0cbf 100644 --- a/pkg/tcp/switcher.go +++ b/pkg/tcp/switcher.go @@ -1,8 +1,6 @@ package tcp import ( - "net" - "github.com/containous/traefik/v2/pkg/safe" ) @@ -12,7 +10,7 @@ type HandlerSwitcher struct { } // ServeTCP forwards the TCP connection to the current active handler -func (s *HandlerSwitcher) ServeTCP(conn net.Conn) { +func (s *HandlerSwitcher) ServeTCP(conn WriteCloser) { handler := s.router.Get() h, ok := handler.(Handler) if ok { diff --git a/pkg/tcp/tls.go b/pkg/tcp/tls.go index 1bfa3d598..6debaad3d 100644 --- a/pkg/tcp/tls.go +++ b/pkg/tcp/tls.go @@ -2,7 +2,6 @@ package tcp import ( "crypto/tls" - "net" ) // TLSHandler handles TLS connections @@ -12,6 +11,6 @@ type TLSHandler struct { } // ServeTCP terminates the TLS connection -func (t *TLSHandler) ServeTCP(conn net.Conn) { +func (t *TLSHandler) ServeTCP(conn WriteCloser) { t.Next.ServeTCP(tls.Server(conn, t.Config)) } From b4c7b90c9ef49ab97db1a33be842ff0704fc4965 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 13 Sep 2019 19:10:04 +0200 Subject: [PATCH 07/19] fix: boolean flag parsing with map. --- pkg/config/flag/flagparser.go | 27 ++++++++++++++++++++++++--- pkg/config/flag/flagparser_test.go | 16 +++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pkg/config/flag/flagparser.go b/pkg/config/flag/flagparser.go index 5638e7649..5a66ef89a 100644 --- a/pkg/config/flag/flagparser.go +++ b/pkg/config/flag/flagparser.go @@ -3,6 +3,7 @@ package flag import ( "fmt" "reflect" + "regexp" "strings" "github.com/containous/traefik/v2/pkg/config/parser" @@ -80,8 +81,8 @@ func (f *flagSet) parseOne() (bool, error) { return true, nil } - n := strings.ToLower(name) - if f.flagTypes[n] == reflect.Bool || f.flagTypes[n] == reflect.Ptr { + flagType := f.getFlagType(name) + if flagType == reflect.Bool || flagType == reflect.Ptr { f.setValue(name, "true") return true, nil } @@ -111,10 +112,30 @@ func (f *flagSet) setValue(name string, value string) { } v, ok := f.values[key] - if ok && f.flagTypes[strings.ToLower(name)] == reflect.Slice { + if ok && f.getFlagType(name) == reflect.Slice { f.values[key] = v + "," + value return } f.values[key] = value } + +func (f *flagSet) getFlagType(name string) reflect.Kind { + neutral := strings.ToLower(name) + + kind, ok := f.flagTypes[neutral] + if ok { + return kind + } + + for n, k := range f.flagTypes { + if strings.Contains(n, parser.MapNamePlaceholder) { + p := strings.NewReplacer(".", `\.`, parser.MapNamePlaceholder, `([^.]+)`).Replace(n) + if regexp.MustCompile(p).MatchString(neutral) { + return k + } + } + } + + return reflect.Invalid +} diff --git a/pkg/config/flag/flagparser_test.go b/pkg/config/flag/flagparser_test.go index 8c45fdaf5..461dfad70 100644 --- a/pkg/config/flag/flagparser_test.go +++ b/pkg/config/flag/flagparser_test.go @@ -250,7 +250,7 @@ func TestParse(t *testing.T) { }, }, { - desc: "map struct with sub-map case senstitive", + desc: "map struct with sub-map case sensitive", args: []string{"--foo.Name1.bar.name2.value=firstValue", "--foo.naMe1.bar.name2.value=secondValue"}, element: &struct { Foo map[string]struct { @@ -273,6 +273,20 @@ func TestParse(t *testing.T) { "traefik.foo.Name1.bar.name2.value": "secondValue", }, }, + { + desc: "pointer of struct and map without explicit value", + args: []string{"--foo.default.bar.fuu"}, + element: &struct { + Foo map[string]struct { + Bar *struct { + Fuu *struct{ Value string } + } + } + }{}, + expected: map[string]string{ + "traefik.foo.default.bar.fuu": "true", + }, + }, { desc: "slice with several flags 2 and different cases.", args: []string{"--foo", "bar", "--Foo", "baz"}, From 8e18d37b3d8a9697071968a1165eb6db41945a81 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 13 Sep 2019 19:28:04 +0200 Subject: [PATCH 08/19] fix: logger and context. --- integration/access_log_test.go | 8 +-- integration/fake_dns_server.go | 11 ++-- integration/grpc_test.go | 2 +- integration/integration_test.go | 4 +- pkg/collector/collector.go | 2 +- pkg/config/static/static_config.go | 6 ++- pkg/healthcheck/healthcheck.go | 30 ++++++----- pkg/log/deprecated.go | 6 --- pkg/log/fields.go | 1 + pkg/middlewares/addprefix/add_prefix.go | 5 +- pkg/middlewares/auth/basic_auth.go | 5 +- pkg/middlewares/auth/digest_auth.go | 5 +- pkg/middlewares/auth/forward.go | 5 +- pkg/middlewares/buffering/buffering.go | 3 +- pkg/middlewares/chain/chain.go | 3 +- .../circuitbreaker/circuit_breaker.go | 3 +- pkg/middlewares/compress/compress.go | 11 ++-- pkg/middlewares/customerrors/custom_errors.go | 14 ++--- .../customerrors/custom_errors_test.go | 3 +- pkg/middlewares/headers/headers.go | 4 +- pkg/middlewares/ipwhitelist/ip_whitelist.go | 14 ++--- pkg/middlewares/metrics/metrics.go | 4 +- pkg/middlewares/middleware.go | 7 ++- .../passtlsclientcert/pass_tls_client_cert.go | 53 ++++++++++--------- pkg/middlewares/pipelining/pipelining.go | 3 +- pkg/middlewares/ratelimiter/rate_limiter.go | 9 ++-- pkg/middlewares/recovery/recovery.go | 10 ++-- pkg/middlewares/redirect/redirect.go | 3 +- pkg/middlewares/redirect/redirect_regex.go | 5 +- pkg/middlewares/redirect/redirect_scheme.go | 5 +- pkg/middlewares/replacepath/replace_path.go | 4 +- .../replacepathregex/replace_path_regex.go | 3 +- pkg/middlewares/retry/retry.go | 10 ++-- pkg/middlewares/stripprefix/strip_prefix.go | 3 +- .../stripprefixregex/strip_prefix_regex.go | 3 +- pkg/middlewares/tracing/entrypoint.go | 3 +- pkg/middlewares/tracing/forwarder.go | 3 +- pkg/provider/acme/provider.go | 8 +-- pkg/provider/docker/config.go | 10 ++-- pkg/provider/file/file.go | 20 +++---- pkg/provider/file/file_test.go | 2 +- pkg/safe/routine.go | 5 +- pkg/server/cookie/cookie.go | 2 +- pkg/server/router/tcp/router_test.go | 1 + pkg/server/server.go | 11 ++-- pkg/server/server_configuration.go | 4 +- pkg/server/server_entrypoint_tcp.go | 14 ++--- pkg/server/service/service.go | 2 +- pkg/tcp/rr_load_balancer.go | 2 +- pkg/tls/tlsmanager.go | 19 ++++--- pkg/tls/tlsmanager_test.go | 22 ++++---- pkg/version/version.go | 19 ++++--- 52 files changed, 231 insertions(+), 183 deletions(-) diff --git a/integration/access_log_test.go b/integration/access_log_test.go index ea21fe500..0e0aa925d 100644 --- a/integration/access_log_test.go +++ b/integration/access_log_test.go @@ -46,7 +46,7 @@ func (s *AccessLogSuite) SetUpSuite(c *check.C) { func (s *AccessLogSuite) TearDownTest(c *check.C) { displayTraefikLogFile(c, traefikTestLogFile) - os.Remove(traefikTestAccessLogFile) + _ = os.Remove(traefikTestAccessLogFile) } func (s *AccessLogSuite) TestAccessLog(c *check.C) { @@ -59,7 +59,7 @@ func (s *AccessLogSuite) TestAccessLog(c *check.C) { defer func() { traefikLog, err := ioutil.ReadFile(traefikTestLogFile) c.Assert(err, checker.IsNil) - log.Info(string(traefikLog)) + log.WithoutContext().Info(string(traefikLog)) }() err := cmd.Start() @@ -233,7 +233,7 @@ func digestParts(resp *http.Response) map[string]string { func getMD5(data string) string { digest := md5.New() if _, err := digest.Write([]byte(data)); err != nil { - log.Error(err) + log.WithoutContext().Error(err) } return fmt.Sprintf("%x", digest.Sum(nil)) } @@ -241,7 +241,7 @@ func getMD5(data string) string { func getCnonce() string { b := make([]byte, 8) if _, err := io.ReadFull(rand.Reader, b); err != nil { - log.Error(err) + log.WithoutContext().Error(err) } return fmt.Sprintf("%x", b)[:16] } diff --git a/integration/fake_dns_server.go b/integration/fake_dns_server.go index 1c4c7a6cd..afae6fe3d 100644 --- a/integration/fake_dns_server.go +++ b/integration/fake_dns_server.go @@ -15,6 +15,8 @@ type handler struct{} // Simplified version of the Challenge Test Server from Boulder // https://github.com/letsencrypt/boulder/blob/a6597b9f120207eff192c3e4107a7e49972a0250/test/challtestsrv/dnsone.go#L40 func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { + logger := log.WithoutContext() + m := new(dns.Msg) m.SetReply(r) m.Compress = false @@ -23,8 +25,9 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { if fakeDNS == "" { fakeDNS = "127.0.0.1" } + for _, q := range r.Question { - log.Infof("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype]) + logger.Infof("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype]) switch q.Qtype { case dns.TypeA: @@ -94,7 +97,7 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { m.Ns = append(m.Ns, auth) if err := w.WriteMsg(m); err != nil { - log.Fatalf("Failed to write message %v", err) + logger.Fatalf("Failed to write message %v", err) } } @@ -106,9 +109,9 @@ func startFakeDNSServer() *dns.Server { } go func() { - log.Infof("Start a fake DNS server.") + log.WithoutContext().Infof("Start a fake DNS server.") if err := srv.ListenAndServe(); err != nil { - log.Fatalf("Failed to set udp listener %v", err) + log.WithoutContext().Fatalf("Failed to set udp listener %v", err) } }() diff --git a/integration/grpc_test.go b/integration/grpc_test.go index 7664c4548..d79ed0009 100644 --- a/integration/grpc_test.go +++ b/integration/grpc_test.go @@ -50,7 +50,7 @@ func (s *myserver) StreamExample(in *helloworld.StreamExampleRequest, server hel } if err := server.Send(&helloworld.StreamExampleReply{Data: string(data)}); err != nil { - log.Error(err) + log.WithoutContext().Error(err) } <-s.stopStreamExample diff --git a/integration/integration_test.go b/integration/integration_test.go index 47f5f0b4f..daca739d5 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -28,7 +28,7 @@ var showLog = flag.Bool("tlog", false, "always show Traefik logs") func Test(t *testing.T) { if !*integration { - log.Info("Integration tests disabled.") + log.WithoutContext().Info("Integration tests disabled.") return } @@ -91,7 +91,7 @@ func (s *BaseSuite) createComposeProject(c *check.C, name string) { ip, _, err := net.ParseCIDR(addr.String()) c.Assert(err, checker.IsNil) if !ip.IsLoopback() && ip.To4() != nil { - os.Setenv("DOCKER_HOST_IP", ip.String()) + _ = os.Setenv("DOCKER_HOST_IP", ip.String()) break } } diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 4a1a6f2a5..6493859a0 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -35,7 +35,7 @@ func Collect(staticConfiguration *static.Configuration) error { return err } - log.Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig) + log.WithoutContext().Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig) hashConf, err := hashstructure.Hash(staticConfiguration, nil) if err != nil { diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index f7caa20cc..38b3569bb 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -238,13 +238,15 @@ func getSafeACMECAServer(caServerSrc string) string { if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") { caServer := strings.Replace(caServerSrc, "v01", "v02", 1) - log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer) + log.WithoutContext(). + Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer) return caServer } if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") { caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1) - log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer) + log.WithoutContext(). + Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer) return caServer } diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index 0e5ff0de7..c1385c399 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -122,37 +122,41 @@ func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backe } func (hc *HealthCheck) execute(ctx context.Context, backend *BackendConfig) { - log.Debugf("Initial health check for backend: %q", backend.name) - hc.checkBackend(backend) + logger := log.FromContext(ctx) + logger.Debugf("Initial health check for backend: %q", backend.name) + + hc.checkBackend(ctx, backend) ticker := time.NewTicker(backend.Interval) defer ticker.Stop() for { select { case <-ctx.Done(): - log.Debugf("Stopping current health check goroutines of backend: %s", backend.name) + logger.Debugf("Stopping current health check goroutines of backend: %s", backend.name) return case <-ticker.C: - log.Debugf("Refreshing health check for backend: %s", backend.name) - hc.checkBackend(backend) + logger.Debugf("Refreshing health check for backend: %s", backend.name) + hc.checkBackend(ctx, backend) } } } -func (hc *HealthCheck) checkBackend(backend *BackendConfig) { +func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig) { + logger := log.FromContext(ctx) + enabledURLs := backend.LB.Servers() var newDisabledURLs []backendURL // FIXME re enable metrics for _, disableURL := range backend.disabledURLs { // FIXME serverUpMetricValue := float64(0) if err := checkHealth(disableURL.url, backend); err == nil { - log.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) if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil { - log.Error(err) + logger.Error(err) } // FIXME serverUpMetricValue = 1 } else { - log.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, disableURL.url.String(), err) newDisabledURLs = append(newDisabledURLs, disableURL) } // FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()} @@ -173,9 +177,9 @@ func (hc *HealthCheck) checkBackend(backend *BackendConfig) { weight = 1 } } - log.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: Remove 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 { - log.Error(err) + logger.Error(err) } backend.disabledURLs = append(backend.disabledURLs, backendURL{enableURL, weight}) // FIXME serverUpMetricValue = 0 @@ -244,10 +248,10 @@ func checkHealth(serverURL *url.URL, backend *BackendConfig) error { } // NewLBStatusUpdater returns a new LbStatusUpdater -func NewLBStatusUpdater(bh BalancerHandler, svinfo *runtime.ServiceInfo) *LbStatusUpdater { +func NewLBStatusUpdater(bh BalancerHandler, info *runtime.ServiceInfo) *LbStatusUpdater { return &LbStatusUpdater{ BalancerHandler: bh, - serviceInfo: svinfo, + serviceInfo: info, } } diff --git a/pkg/log/deprecated.go b/pkg/log/deprecated.go index 7c7f573b3..2bdf735ed 100644 --- a/pkg/log/deprecated.go +++ b/pkg/log/deprecated.go @@ -62,12 +62,6 @@ func Panic(args ...interface{}) { mainLogger.Panic(args...) } -// Panicf logs a message at level Panic on the standard logger. -// Deprecated -func Panicf(format string, args ...interface{}) { - mainLogger.Panicf(format, args...) -} - // Fatal logs a message at level Fatal on the standard logger. // Deprecated func Fatal(args ...interface{}) { diff --git a/pkg/log/fields.go b/pkg/log/fields.go index 97e51412d..f84b00498 100644 --- a/pkg/log/fields.go +++ b/pkg/log/fields.go @@ -12,4 +12,5 @@ const ( MetricsProviderName = "metricsProviderName" TracingProviderName = "tracingProviderName" ServerName = "serverName" + TLSStoreName = "tlsStoreName" ) diff --git a/pkg/middlewares/addprefix/add_prefix.go b/pkg/middlewares/addprefix/add_prefix.go index 02374ea89..655e874a8 100644 --- a/pkg/middlewares/addprefix/add_prefix.go +++ b/pkg/middlewares/addprefix/add_prefix.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -24,7 +25,7 @@ type addPrefix struct { // New creates a new handler. func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") var result *addPrefix if len(config.Prefix) > 0 { @@ -45,7 +46,7 @@ func (ap *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) { } func (ap *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), ap.name, typeName) + logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), ap.name, typeName)) oldURLPath := req.URL.Path req.URL.Path = ap.prefix + req.URL.Path diff --git a/pkg/middlewares/auth/basic_auth.go b/pkg/middlewares/auth/basic_auth.go index 62eca1cb9..ee857d05a 100644 --- a/pkg/middlewares/auth/basic_auth.go +++ b/pkg/middlewares/auth/basic_auth.go @@ -9,6 +9,7 @@ import ( goauth "github.com/abbot/go-http-auth" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/middlewares/accesslog" "github.com/containous/traefik/v2/pkg/tracing" @@ -30,7 +31,7 @@ type basicAuth struct { // NewBasic creates a basicAuth middleware. func NewBasic(ctx context.Context, next http.Handler, authConfig dynamic.BasicAuth, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, basicTypeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, basicTypeName)).Debug("Creating middleware") users, err := getUsers(authConfig.UsersFile, authConfig.Users, basicUserParser) if err != nil { return nil, err @@ -58,7 +59,7 @@ func (b *basicAuth) GetTracingInformation() (string, ext.SpanKindEnum) { } func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), b.name, basicTypeName) + logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), b.name, basicTypeName)) if username := b.auth.CheckAuth(req); username == "" { logger.Debug("Authentication failed") diff --git a/pkg/middlewares/auth/digest_auth.go b/pkg/middlewares/auth/digest_auth.go index 25cb29cda..0a43d45c1 100644 --- a/pkg/middlewares/auth/digest_auth.go +++ b/pkg/middlewares/auth/digest_auth.go @@ -9,6 +9,7 @@ import ( goauth "github.com/abbot/go-http-auth" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/middlewares/accesslog" "github.com/containous/traefik/v2/pkg/tracing" @@ -30,7 +31,7 @@ type digestAuth struct { // NewDigest creates a digest auth middleware. func NewDigest(ctx context.Context, next http.Handler, authConfig dynamic.DigestAuth, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, digestTypeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, digestTypeName)).Debug("Creating middleware") users, err := getUsers(authConfig.UsersFile, authConfig.Users, digestUserParser) if err != nil { return nil, err @@ -58,7 +59,7 @@ func (d *digestAuth) GetTracingInformation() (string, ext.SpanKindEnum) { } func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), d.name, digestTypeName) + logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), d.name, digestTypeName)) if username, _ := d.auth.CheckAuth(req); username == "" { logger.Debug("Digest authentication failed") diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index 85891f76e..f1b4421bf 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -34,7 +35,7 @@ type forwardAuth struct { // NewForward creates a forward auth middleware. func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAuth, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, forwardedTypeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, forwardedTypeName)).Debug("Creating middleware") fa := &forwardAuth{ address: config.Address, @@ -61,7 +62,7 @@ func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) { } func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), fa.name, forwardedTypeName) + logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), fa.name, forwardedTypeName)) // Ensure our request client does not follow redirects httpClient := http.Client{ diff --git a/pkg/middlewares/buffering/buffering.go b/pkg/middlewares/buffering/buffering.go index e6183a723..069e710a7 100644 --- a/pkg/middlewares/buffering/buffering.go +++ b/pkg/middlewares/buffering/buffering.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -22,7 +23,7 @@ type buffer struct { // New creates a buffering middleware. func New(ctx context.Context, next http.Handler, config dynamic.Buffering, name string) (http.Handler, error) { - logger := middlewares.GetLogger(ctx, name, typeName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)) logger.Debug("Creating middleware") logger.Debugf("Setting up buffering: request limits: %d (mem), %d (max), response limits: %d (mem), %d (max) with retry: '%s'", config.MemRequestBodyBytes, config.MaxRequestBodyBytes, config.MemResponseBodyBytes, config.MaxResponseBodyBytes, config.RetryExpression) diff --git a/pkg/middlewares/chain/chain.go b/pkg/middlewares/chain/chain.go index ee3668bbd..bf227b069 100644 --- a/pkg/middlewares/chain/chain.go +++ b/pkg/middlewares/chain/chain.go @@ -6,6 +6,7 @@ import ( "github.com/containous/alice" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" ) @@ -19,7 +20,7 @@ type chainBuilder interface { // New creates a chain middleware func New(ctx context.Context, next http.Handler, config dynamic.Chain, builder chainBuilder, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") middlewareChain := builder.BuildChain(ctx, config.Middlewares) return middlewareChain.Then(next) diff --git a/pkg/middlewares/circuitbreaker/circuit_breaker.go b/pkg/middlewares/circuitbreaker/circuit_breaker.go index dc561b465..c4c2c5cf1 100644 --- a/pkg/middlewares/circuitbreaker/circuit_breaker.go +++ b/pkg/middlewares/circuitbreaker/circuit_breaker.go @@ -25,7 +25,7 @@ type circuitBreaker struct { func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.CircuitBreaker, name string) (http.Handler, error) { expression := confCircuitBreaker.Expression - logger := middlewares.GetLogger(ctx, name, typeName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)) logger.Debug("Creating middleware") logger.Debug("Setting up with expression: %s", expression) @@ -56,6 +56,5 @@ func (c *circuitBreaker) GetTracingInformation() (string, ext.SpanKindEnum) { } func (c *circuitBreaker) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - middlewares.GetLogger(req.Context(), c.name, typeName).Debug("Entering middleware") c.circuitBreaker.ServeHTTP(rw, req) } diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go index 2e9be3b81..7269d2c39 100644 --- a/pkg/middlewares/compress/compress.go +++ b/pkg/middlewares/compress/compress.go @@ -7,10 +7,10 @@ import ( "strings" "github.com/NYTimes/gziphandler" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" - "github.com/sirupsen/logrus" ) const ( @@ -25,7 +25,7 @@ type compress struct { // New creates a new compress middleware. func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &compress{ next: next, @@ -38,7 +38,8 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if strings.HasPrefix(contentType, "application/grpc") { c.next.ServeHTTP(rw, req) } else { - gzipHandler(c.next, middlewares.GetLogger(req.Context(), c.name, typeName)).ServeHTTP(rw, req) + ctx := middlewares.GetLoggerCtx(req.Context(), c.name, typeName) + gzipHandler(ctx, c.next).ServeHTTP(rw, req) } } @@ -46,12 +47,12 @@ func (c *compress) GetTracingInformation() (string, ext.SpanKindEnum) { return c.name, tracing.SpanKindNoneEnum } -func gzipHandler(h http.Handler, logger logrus.FieldLogger) http.Handler { +func gzipHandler(ctx context.Context, h http.Handler) http.Handler { wrapper, err := gziphandler.GzipHandlerWithOpts( gziphandler.CompressionLevel(gzip.DefaultCompression), gziphandler.MinSize(gziphandler.DefaultMinSize)) if err != nil { - logger.Error(err) + log.FromContext(ctx).Error(err) } return wrapper(h) diff --git a/pkg/middlewares/customerrors/custom_errors.go b/pkg/middlewares/customerrors/custom_errors.go index c88c90c2f..72c70a631 100644 --- a/pkg/middlewares/customerrors/custom_errors.go +++ b/pkg/middlewares/customerrors/custom_errors.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/containous/traefik/v2/pkg/types" @@ -43,7 +44,7 @@ type customErrors struct { // New creates a new custom error pages middleware. func New(ctx context.Context, next http.Handler, config dynamic.ErrorPage, serviceBuilder serviceBuilder, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") httpCodeRanges, err := types.NewHTTPCodeRanges(config.Status) if err != nil { @@ -69,7 +70,8 @@ func (c *customErrors) GetTracingInformation() (string, ext.SpanKindEnum) { } func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), c.name, typeName) + ctx := middlewares.GetLoggerCtx(req.Context(), c.name, typeName) + logger := log.FromContext(ctx) if c.backendHandler == nil { logger.Error("Error pages: no backend handler.") @@ -78,7 +80,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { return } - recorder := newResponseRecorder(rw, middlewares.GetLogger(context.Background(), "test", typeName)) + recorder := newResponseRecorder(ctx, rw) c.next.ServeHTTP(recorder, req) // check the recorder code against the configured http status code ranges @@ -103,7 +105,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { return } - recorderErrorPage := newResponseRecorder(rw, middlewares.GetLogger(context.Background(), "test", typeName)) + recorderErrorPage := newResponseRecorder(ctx, rw) utils.CopyHeaders(pageReq.Header, req.Header) c.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context())) @@ -151,13 +153,13 @@ type responseRecorder interface { } // newResponseRecorder returns an initialized responseRecorder. -func newResponseRecorder(rw http.ResponseWriter, logger logrus.FieldLogger) responseRecorder { +func newResponseRecorder(ctx context.Context, rw http.ResponseWriter) responseRecorder { recorder := &responseRecorderWithoutCloseNotify{ HeaderMap: make(http.Header), Body: new(bytes.Buffer), Code: http.StatusOK, responseWriter: rw, - logger: logger, + logger: log.FromContext(ctx), } if _, ok := rw.(http.CloseNotifier); ok { return &responseRecorderWithCloseNotify{recorder} diff --git a/pkg/middlewares/customerrors/custom_errors_test.go b/pkg/middlewares/customerrors/custom_errors_test.go index 19aee2437..5dbb22f30 100644 --- a/pkg/middlewares/customerrors/custom_errors_test.go +++ b/pkg/middlewares/customerrors/custom_errors_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/containous/traefik/v2/pkg/config/dynamic" - "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/testhelpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -151,7 +150,7 @@ func TestNewResponseRecorder(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - rec := newResponseRecorder(test.rw, middlewares.GetLogger(context.Background(), "test", typeName)) + rec := newResponseRecorder(context.Background(), test.rw) assert.IsType(t, rec, test.expected) }) } diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go index 63a623e13..149b38219 100644 --- a/pkg/middlewares/headers/headers.go +++ b/pkg/middlewares/headers/headers.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -27,8 +28,9 @@ type headers struct { // New creates a Headers middleware. func New(ctx context.Context, next http.Handler, config dynamic.Headers, name string) (http.Handler, error) { // HeaderMiddleware -> SecureMiddleWare -> next - logger := middlewares.GetLogger(ctx, name, typeName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)) logger.Debug("Creating middleware") + hasSecureHeaders := config.HasSecureHeadersDefined() hasCustomHeaders := config.HasCustomHeadersDefined() hasCorsHeaders := config.HasCorsHeadersDefined() diff --git a/pkg/middlewares/ipwhitelist/ip_whitelist.go b/pkg/middlewares/ipwhitelist/ip_whitelist.go index 20c9bd7bc..5e7468500 100644 --- a/pkg/middlewares/ipwhitelist/ip_whitelist.go +++ b/pkg/middlewares/ipwhitelist/ip_whitelist.go @@ -8,10 +8,10 @@ import ( "github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/ip" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" - "github.com/sirupsen/logrus" ) const ( @@ -28,7 +28,7 @@ type ipWhiteLister struct { // New builds a new IPWhiteLister given a list of CIDR-Strings to whitelist func New(ctx context.Context, next http.Handler, config dynamic.IPWhiteList, name string) (http.Handler, error) { - logger := middlewares.GetLogger(ctx, name, typeName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)) logger.Debug("Creating middleware") if len(config.SourceRange) == 0 { @@ -46,6 +46,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.IPWhiteList, nam } logger.Debugf("Setting up IPWhiteLister with sourceRange: %s", config.SourceRange) + return &ipWhiteLister{ strategy: strategy, whiteLister: checker, @@ -59,14 +60,15 @@ func (wl *ipWhiteLister) GetTracingInformation() (string, ext.SpanKindEnum) { } func (wl *ipWhiteLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), wl.name, typeName) + ctx := middlewares.GetLoggerCtx(req.Context(), wl.name, typeName) + logger := log.FromContext(ctx) err := wl.whiteLister.IsAuthorized(wl.strategy.GetIP(req)) if err != nil { logMessage := fmt.Sprintf("rejecting request %+v: %v", req, err) logger.Debug(logMessage) tracing.SetErrorWithEvent(req, logMessage) - reject(logger, rw) + reject(ctx, rw) return } logger.Debugf("Accept %s: %+v", wl.strategy.GetIP(req), req) @@ -74,12 +76,12 @@ func (wl *ipWhiteLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { wl.next.ServeHTTP(rw, req) } -func reject(logger logrus.FieldLogger, rw http.ResponseWriter) { +func reject(ctx context.Context, rw http.ResponseWriter) { statusCode := http.StatusForbidden rw.WriteHeader(statusCode) _, err := rw.Write([]byte(http.StatusText(statusCode))) if err != nil { - logger.Error(err) + log.FromContext(ctx).Error(err) } } diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go index 6f86095e7..b5f36640a 100644 --- a/pkg/middlewares/metrics/metrics.go +++ b/pkg/middlewares/metrics/metrics.go @@ -39,7 +39,7 @@ type metricsMiddleware struct { // NewEntryPointMiddleware creates a new metrics middleware for an Entrypoint. func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, entryPointName string) http.Handler { - middlewares.GetLogger(ctx, nameEntrypoint, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Debug("Creating middleware") return &metricsMiddleware{ next: next, @@ -52,7 +52,7 @@ func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry me // NewServiceMiddleware creates a new metrics middleware for a Service. func NewServiceMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, serviceName string) http.Handler { - middlewares.GetLogger(ctx, nameService, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, nameService, typeName)).Debug("Creating middleware") return &metricsMiddleware{ next: next, diff --git a/pkg/middlewares/middleware.go b/pkg/middlewares/middleware.go index 2b76b2bc8..c340151cf 100644 --- a/pkg/middlewares/middleware.go +++ b/pkg/middlewares/middleware.go @@ -4,10 +4,9 @@ import ( "context" "github.com/containous/traefik/v2/pkg/log" - "github.com/sirupsen/logrus" ) -// GetLogger creates a logger configured with the middleware fields. -func GetLogger(ctx context.Context, middleware string, middlewareType string) logrus.FieldLogger { - return log.FromContext(ctx).WithField(log.MiddlewareName, middleware).WithField(log.MiddlewareType, middlewareType) +// GetLoggerCtx creates a logger context with the middleware fields. +func GetLoggerCtx(ctx context.Context, middleware string, middlewareType string) context.Context { + return log.With(ctx, log.Str(log.MiddlewareName, middleware), log.Str(log.MiddlewareType, middlewareType)) } diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go index 64d6f2fea..a07bb9a73 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go @@ -16,7 +16,6 @@ import ( "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" - "github.com/sirupsen/logrus" ) const ( @@ -66,7 +65,7 @@ type passTLSClientCert struct { // New constructs a new PassTLSClientCert instance from supplied frontend header struct. func New(ctx context.Context, next http.Handler, config dynamic.PassTLSClientCert, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &passTLSClientCert{ next: next, @@ -104,11 +103,13 @@ func (p *passTLSClientCert) GetTracingInformation() (string, ext.SpanKindEnum) { } func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := middlewares.GetLogger(req.Context(), p.name, typeName) - p.modifyRequestHeaders(logger, req) + ctx := middlewares.GetLoggerCtx(req.Context(), p.name, typeName) + + p.modifyRequestHeaders(ctx, req) p.next.ServeHTTP(rw, req) } -func getDNInfo(prefix string, options *DistinguishedNameOptions, cs *pkix.Name) string { + +func getDNInfo(ctx context.Context, prefix string, options *DistinguishedNameOptions, cs *pkix.Name) string { if options == nil { return "" } @@ -124,27 +125,27 @@ func getDNInfo(prefix string, options *DistinguishedNameOptions, cs *pkix.Name) } if options.CountryName { - writeParts(content, cs.Country, "C") + writeParts(ctx, content, cs.Country, "C") } if options.StateOrProvinceName { - writeParts(content, cs.Province, "ST") + writeParts(ctx, content, cs.Province, "ST") } if options.LocalityName { - writeParts(content, cs.Locality, "L") + writeParts(ctx, content, cs.Locality, "L") } if options.OrganizationName { - writeParts(content, cs.Organization, "O") + writeParts(ctx, content, cs.Organization, "O") } if options.SerialNumber { - writePart(content, cs.SerialNumber, "SN") + writePart(ctx, content, cs.SerialNumber, "SN") } if options.CommonName { - writePart(content, cs.CommonName, "CN") + writePart(ctx, content, cs.CommonName, "CN") } if content.Len() > 0 { @@ -154,24 +155,24 @@ func getDNInfo(prefix string, options *DistinguishedNameOptions, cs *pkix.Name) return "" } -func writeParts(content io.StringWriter, entries []string, prefix string) { +func writeParts(ctx context.Context, content io.StringWriter, entries []string, prefix string) { for _, entry := range entries { - writePart(content, entry, prefix) + writePart(ctx, content, entry, prefix) } } -func writePart(content io.StringWriter, entry string, prefix string) { +func writePart(ctx context.Context, content io.StringWriter, entry string, prefix string) { if len(entry) > 0 { _, err := content.WriteString(fmt.Sprintf("%s=%s,", prefix, entry)) if err != nil { - log.Error(err) + log.FromContext(ctx).Error(err) } } } // getXForwardedTLSClientCertInfo Build a string with the wanted client certificates information // like Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s; -func (p *passTLSClientCert) getXForwardedTLSClientCertInfo(certs []*x509.Certificate) string { +func (p *passTLSClientCert) getXForwardedTLSClientCertInfo(ctx context.Context, certs []*x509.Certificate) string { var headerValues []string for _, peerCert := range certs { @@ -181,12 +182,12 @@ func (p *passTLSClientCert) getXForwardedTLSClientCertInfo(certs []*x509.Certifi var na string if p.info != nil { - subject := getDNInfo("Subject", p.info.subject, &peerCert.Subject) + subject := getDNInfo(ctx, "Subject", p.info.subject, &peerCert.Subject) if len(subject) > 0 { values = append(values, subject) } - issuer := getDNInfo("Issuer", p.info.issuer, &peerCert.Issuer) + issuer := getDNInfo(ctx, "Issuer", p.info.issuer, &peerCert.Issuer) if len(issuer) > 0 { values = append(values, issuer) } @@ -217,10 +218,12 @@ func (p *passTLSClientCert) getXForwardedTLSClientCertInfo(certs []*x509.Certifi } // modifyRequestHeaders set the wanted headers with the certificates information. -func (p *passTLSClientCert) modifyRequestHeaders(logger logrus.FieldLogger, r *http.Request) { +func (p *passTLSClientCert) modifyRequestHeaders(ctx context.Context, r *http.Request) { + logger := log.FromContext(ctx) + if p.pem { if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { - r.Header.Set(xForwardedTLSClientCert, getXForwardedTLSClientCert(logger, r.TLS.PeerCertificates)) + r.Header.Set(xForwardedTLSClientCert, getXForwardedTLSClientCert(ctx, r.TLS.PeerCertificates)) } else { logger.Warn("Tried to extract a certificate on a request without mutual TLS") } @@ -228,7 +231,7 @@ func (p *passTLSClientCert) modifyRequestHeaders(logger logrus.FieldLogger, r *h if p.info != nil { if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { - headerContent := p.getXForwardedTLSClientCertInfo(r.TLS.PeerCertificates) + headerContent := p.getXForwardedTLSClientCertInfo(ctx, r.TLS.PeerCertificates) r.Header.Set(xForwardedTLSClientCertInfo, url.QueryEscape(headerContent)) } else { logger.Warn("Tried to extract a certificate on a request without mutual TLS") @@ -248,22 +251,22 @@ func sanitize(cert []byte) string { } // extractCertificate extract the certificate from the request. -func extractCertificate(logger logrus.FieldLogger, cert *x509.Certificate) string { +func extractCertificate(ctx context.Context, cert *x509.Certificate) string { b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw} certPEM := pem.EncodeToMemory(&b) if certPEM == nil { - logger.Error("Cannot extract the certificate content") + log.FromContext(ctx).Error("Cannot extract the certificate content") return "" } return sanitize(certPEM) } // getXForwardedTLSClientCert Build a string with the client certificates. -func getXForwardedTLSClientCert(logger logrus.FieldLogger, certs []*x509.Certificate) string { +func getXForwardedTLSClientCert(ctx context.Context, certs []*x509.Certificate) string { var headerValues []string for _, peerCert := range certs { - headerValues = append(headerValues, extractCertificate(logger, peerCert)) + headerValues = append(headerValues, extractCertificate(ctx, peerCert)) } return strings.Join(headerValues, ",") diff --git a/pkg/middlewares/pipelining/pipelining.go b/pkg/middlewares/pipelining/pipelining.go index e2cf2898a..a4e14110a 100644 --- a/pkg/middlewares/pipelining/pipelining.go +++ b/pkg/middlewares/pipelining/pipelining.go @@ -6,6 +6,7 @@ import ( "net" "net/http" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" ) @@ -20,7 +21,7 @@ type pipelining struct { // New returns a new pipelining instance func New(ctx context.Context, next http.Handler, name string) http.Handler { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &pipelining{ next: next, diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go index 73682c200..4e51ec3c9 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter.go +++ b/pkg/middlewares/ratelimiter/rate_limiter.go @@ -90,7 +90,8 @@ func (rl *rateLimiter) GetTracingInformation() (string, ext.SpanKindEnum) { } func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) { - logger := middlewares.GetLogger(r.Context(), rl.name, typeName) + ctx := middlewares.GetLoggerCtx(r.Context(), rl.name, typeName) + logger := log.FromContext(ctx) source, amount, err := rl.sourceMatcher.Extract(r) if err != nil { @@ -127,7 +128,7 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) { delay := res.Delay() if delay > rl.maxDelay { res.Cancel() - rl.serveDelayError(w, r, delay) + rl.serveDelayError(ctx, w, r, delay) return } @@ -135,12 +136,12 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) { rl.next.ServeHTTP(w, r) } -func (rl *rateLimiter) serveDelayError(w http.ResponseWriter, r *http.Request, delay time.Duration) { +func (rl *rateLimiter) serveDelayError(ctx context.Context, w http.ResponseWriter, r *http.Request, delay time.Duration) { w.Header().Set("Retry-After", fmt.Sprintf("%.0f", delay.Seconds())) w.Header().Set("X-Retry-In", delay.String()) w.WriteHeader(http.StatusTooManyRequests) if _, err := w.Write([]byte(http.StatusText(http.StatusTooManyRequests))); err != nil { - middlewares.GetLogger(r.Context(), rl.name, typeName).Errorf("could not serve 429: %v", err) + log.FromContext(ctx).Errorf("could not serve 429: %v", err) } } diff --git a/pkg/middlewares/recovery/recovery.go b/pkg/middlewares/recovery/recovery.go index 054b2d402..563d52e66 100644 --- a/pkg/middlewares/recovery/recovery.go +++ b/pkg/middlewares/recovery/recovery.go @@ -4,8 +4,8 @@ import ( "context" "net/http" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" - "github.com/sirupsen/logrus" ) const ( @@ -19,7 +19,7 @@ type recovery struct { // New creates recovery middleware. func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &recovery{ next: next, @@ -28,13 +28,13 @@ func New(ctx context.Context, next http.Handler, name string) (http.Handler, err } func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - defer recoverFunc(middlewares.GetLogger(req.Context(), re.name, typeName), rw) + defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw) re.next.ServeHTTP(rw, req) } -func recoverFunc(logger logrus.FieldLogger, rw http.ResponseWriter) { +func recoverFunc(ctx context.Context, rw http.ResponseWriter) { if err := recover(); err != nil { - logger.Errorf("Recovered from panic in http handler: %+v", err) + log.FromContext(ctx).Errorf("Recovered from panic in http handler: %+v", err) http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } } diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index 541c4ab7c..0e87013c3 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -2,7 +2,6 @@ package redirect import ( "bytes" - "context" "html/template" "io" "net/http" @@ -25,7 +24,7 @@ type redirect struct { } // New creates a Redirect middleware. -func newRedirect(_ context.Context, next http.Handler, regex string, replacement string, permanent bool, name string) (http.Handler, error) { +func newRedirect(next http.Handler, regex string, replacement string, permanent bool, name string) (http.Handler, error) { re, err := regexp.Compile(regex) if err != nil { return nil, err diff --git a/pkg/middlewares/redirect/redirect_regex.go b/pkg/middlewares/redirect/redirect_regex.go index 0c8f6406e..7e366574a 100644 --- a/pkg/middlewares/redirect/redirect_regex.go +++ b/pkg/middlewares/redirect/redirect_regex.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" ) @@ -14,9 +15,9 @@ const ( // NewRedirectRegex creates a redirect middleware. func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.RedirectRegex, name string) (http.Handler, error) { - logger := middlewares.GetLogger(ctx, name, typeRegexName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeRegexName)) logger.Debug("Creating middleware") logger.Debugf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement) - return newRedirect(ctx, next, conf.Regex, conf.Replacement, conf.Permanent, name) + return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, name) } diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go index 0ffd5d704..92523c70e 100644 --- a/pkg/middlewares/redirect/redirect_scheme.go +++ b/pkg/middlewares/redirect/redirect_scheme.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" ) @@ -16,7 +17,7 @@ const ( // NewRedirectScheme creates a new RedirectScheme middleware. func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.RedirectScheme, name string) (http.Handler, error) { - logger := middlewares.GetLogger(ctx, name, typeSchemeName) + logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeSchemeName)) logger.Debug("Creating middleware") logger.Debugf("Setting up redirection to %s %s", conf.Scheme, conf.Port) @@ -29,5 +30,5 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi port = ":" + conf.Port } - return newRedirect(ctx, next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, name) + return newRedirect(next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, name) } diff --git a/pkg/middlewares/replacepath/replace_path.go b/pkg/middlewares/replacepath/replace_path.go index a53cc9a54..bec16827d 100644 --- a/pkg/middlewares/replacepath/replace_path.go +++ b/pkg/middlewares/replacepath/replace_path.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -25,7 +26,7 @@ type replacePath struct { // New creates a new replace path middleware. func New(ctx context.Context, next http.Handler, config dynamic.ReplacePath, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &replacePath{ next: next, @@ -42,5 +43,6 @@ func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) { req.Header.Add(ReplacedPathHeader, req.URL.Path) req.URL.Path = r.path req.RequestURI = req.URL.RequestURI() + r.next.ServeHTTP(rw, req) } diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go index 89d3b062a..c23820fbd 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/middlewares/replacepath" "github.com/containous/traefik/v2/pkg/tracing" @@ -28,7 +29,7 @@ type replacePathRegex struct { // New creates a new replace path regex middleware. func New(ctx context.Context, next http.Handler, config dynamic.ReplacePathRegex, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") exp, err := regexp.Compile(strings.TrimSpace(config.Regex)) if err != nil { diff --git a/pkg/middlewares/retry/retry.go b/pkg/middlewares/retry/retry.go index 05fd158f1..937ef1b04 100644 --- a/pkg/middlewares/retry/retry.go +++ b/pkg/middlewares/retry/retry.go @@ -10,6 +10,7 @@ import ( "net/http/httptrace" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -43,8 +44,7 @@ type retry struct { // New returns a new retry middleware. func New(ctx context.Context, next http.Handler, config dynamic.Retry, listener Listener, name string) (http.Handler, error) { - logger := middlewares.GetLogger(ctx, name, typeName) - logger.Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") if config.Attempts <= 0 { return nil, fmt.Errorf("incorrect (or empty) value for attempt (%d)", config.Attempts) @@ -94,8 +94,10 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } attempts++ - logger := middlewares.GetLogger(req.Context(), r.name, typeName) - logger.Debugf("New attempt %d for request: %v", attempts, req.URL) + + log.FromContext(middlewares.GetLoggerCtx(req.Context(), r.name, typeName)). + Debugf("New attempt %d for request: %v", attempts, req.URL) + r.listener.Retried(req, attempts) } } diff --git a/pkg/middlewares/stripprefix/strip_prefix.go b/pkg/middlewares/stripprefix/strip_prefix.go index 31bc5ae90..b8008689a 100644 --- a/pkg/middlewares/stripprefix/strip_prefix.go +++ b/pkg/middlewares/stripprefix/strip_prefix.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -26,7 +27,7 @@ type stripPrefix struct { // New creates a new strip prefix middleware. func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &stripPrefix{ prefixes: config.Prefixes, next: next, diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go index 6cc82f178..4024ac4f0 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/middlewares/stripprefix" "github.com/containous/traefik/v2/pkg/tracing" @@ -26,7 +27,7 @@ type stripPrefixRegex struct { // New builds a new StripPrefixRegex middleware. func New(ctx context.Context, next http.Handler, config dynamic.StripPrefixRegex, name string) (http.Handler, error) { - middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") stripPrefix := stripPrefixRegex{ next: next, diff --git a/pkg/middlewares/tracing/entrypoint.go b/pkg/middlewares/tracing/entrypoint.go index f7eeb7dc9..195cc994f 100644 --- a/pkg/middlewares/tracing/entrypoint.go +++ b/pkg/middlewares/tracing/entrypoint.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/containous/alice" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go" @@ -17,7 +18,7 @@ const ( // NewEntryPoint creates a new middleware that the incoming request. func NewEntryPoint(ctx context.Context, t *tracing.Tracing, entryPointName string, next http.Handler) http.Handler { - middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug("Creating middleware") + log.FromContext(middlewares.GetLoggerCtx(ctx, "tracing", entryPointTypeName)).Debug("Creating middleware") return &entryPointMiddleware{ entryPoint: entryPointName, diff --git a/pkg/middlewares/tracing/forwarder.go b/pkg/middlewares/tracing/forwarder.go index 25f8ba8bc..4c689a06a 100644 --- a/pkg/middlewares/tracing/forwarder.go +++ b/pkg/middlewares/tracing/forwarder.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/middlewares" "github.com/containous/traefik/v2/pkg/tracing" "github.com/opentracing/opentracing-go/ext" @@ -21,7 +22,7 @@ type forwarderMiddleware struct { // NewForwarder creates a new forwarder middleware that traces the outgoing request. func NewForwarder(ctx context.Context, router, service string, next http.Handler) http.Handler { - middlewares.GetLogger(ctx, "tracing", forwarderTypeName). + log.FromContext(middlewares.GetLoggerCtx(ctx, "tracing", forwarderTypeName)). Debugf("Added outgoing tracing middleware %s", service) return &forwarderMiddleware{ diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index 2025eb5ff..ecf28a363 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -350,17 +350,19 @@ func (p *Provider) watchNewDomains(ctx context.Context) { if route.TLS == nil || route.TLS.CertResolver != p.ResolverName { continue } + ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule)) + logger := log.FromContext(ctxRouter) tlsStore := "default" if len(route.TLS.Domains) > 0 { for _, domain := range route.TLS.Domains { if domain.Main != dns01.UnFqdn(domain.Main) { - log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main) + logger.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main) } for _, san := range domain.SANs { if san != dns01.UnFqdn(san) { - log.Warnf("FQDN detected, please remove the trailing dot: %s", san) + logger.Warnf("FQDN detected, please remove the trailing dot: %s", san) } } } @@ -378,7 +380,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) { } else { domains, err := rules.ParseHostSNI(route.Rule) if err != nil { - log.FromContext(ctxRouter).Errorf("Error parsing domains in provider ACME: %v", err) + logger.Errorf("Error parsing domains in provider ACME: %v", err) continue } p.resolveDomains(ctxRouter, domains, tlsStore) diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 86f097665..9c3c18bf0 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -85,8 +85,9 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d } } - for _, service := range configuration.Services { - err := p.addServerTCP(ctx, container, service.LoadBalancer) + for name, service := range configuration.Services { + ctxSvc := log.With(ctx, log.Str(log.ServiceName, name)) + err := p.addServerTCP(ctxSvc, container, service.LoadBalancer) if err != nil { return err } @@ -107,8 +108,9 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, container dock } } - for _, service := range configuration.Services { - err := p.addServer(ctx, container, service.LoadBalancer) + for name, service := range configuration.Services { + ctxSvc := log.With(ctx, log.Str(log.ServiceName, name)) + err := p.addServer(ctxSvc, container, service.LoadBalancer) if err != nil { return err } diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go index 2ab456373..667be1205 100644 --- a/pkg/provider/file/file.go +++ b/pkg/provider/file/file.go @@ -85,7 +85,7 @@ func (p *Provider) BuildConfiguration() (*dynamic.Configuration, error) { } if len(p.Filename) > 0 { - return p.loadFileConfig(p.Filename, true) + return p.loadFileConfig(ctx, p.Filename, true) } return nil, errors.New("error using file configuration provider, neither filename or directory defined") @@ -156,11 +156,11 @@ func sendConfigToChannel(configurationChan chan<- dynamic.Message, configuration } } -func (p *Provider) loadFileConfig(filename string, parseTemplate bool) (*dynamic.Configuration, error) { +func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTemplate bool) (*dynamic.Configuration, error) { var err error var configuration *dynamic.Configuration if parseTemplate { - configuration, err = p.CreateConfiguration(filename, template.FuncMap{}, false) + configuration, err = p.CreateConfiguration(ctx, filename, template.FuncMap{}, false) } else { configuration, err = p.DecodeConfiguration(filename) } @@ -169,25 +169,25 @@ func (p *Provider) loadFileConfig(filename string, parseTemplate bool) (*dynamic } if configuration.TLS != nil { - configuration.TLS.Certificates = flattenCertificates(configuration.TLS) + configuration.TLS.Certificates = flattenCertificates(ctx, configuration.TLS) } return configuration, nil } -func flattenCertificates(tlsConfig *dynamic.TLSConfiguration) []*tls.CertAndStores { +func flattenCertificates(ctx context.Context, tlsConfig *dynamic.TLSConfiguration) []*tls.CertAndStores { var certs []*tls.CertAndStores for _, cert := range tlsConfig.Certificates { content, err := cert.Certificate.CertFile.Read() if err != nil { - log.Error(err) + log.FromContext(ctx).Error(err) continue } cert.Certificate.CertFile = tls.FileOrContent(string(content)) content, err = cert.Certificate.KeyFile.Read() if err != nil { - log.Error(err) + log.FromContext(ctx).Error(err) continue } cert.Certificate.KeyFile = tls.FileOrContent(string(content)) @@ -243,7 +243,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st } var c *dynamic.Configuration - c, err = p.loadFileConfig(filepath.Join(directory, item.Name()), true) + c, err = p.loadFileConfig(ctx, filepath.Join(directory, item.Name()), true) if err != nil { return configuration, err } @@ -331,7 +331,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st } // CreateConfiguration creates a provider configuration from content using templating. -func (p *Provider) CreateConfiguration(filename string, funcMap template.FuncMap, templateObjects interface{}) (*dynamic.Configuration, error) { +func (p *Provider) CreateConfiguration(ctx context.Context, filename string, funcMap template.FuncMap, templateObjects interface{}) (*dynamic.Configuration, error) { tmplContent, err := readFile(filename) if err != nil { return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err) @@ -359,7 +359,7 @@ func (p *Provider) CreateConfiguration(filename string, funcMap template.FuncMap var renderedTemplate = buffer.String() if p.DebugLogGeneratedTemplate { - logger := log.WithoutContext().WithField(log.ProviderName, providerName) + logger := log.FromContext(ctx) logger.Debugf("Template content: %s", tmplContent) logger.Debugf("Rendering results: %s", renderedTemplate) } diff --git a/pkg/provider/file/file_test.go b/pkg/provider/file/file_test.go index fab3f6d1d..f4892823f 100644 --- a/pkg/provider/file/file_test.go +++ b/pkg/provider/file/file_test.go @@ -46,7 +46,7 @@ func TestTLSContent(t *testing.T) { require.NoError(t, err) provider := &Provider{} - configuration, err := provider.loadFileConfig(fileConfig.Name(), true) + configuration, err := provider.loadFileConfig(context.Background(), fileConfig.Name(), true) require.NoError(t, err) require.Equal(t, "CONTENT", configuration.TLS.Certificates[0].Certificate.CertFile.String()) diff --git a/pkg/safe/routine.go b/pkg/safe/routine.go index 83f33b872..f02cfda9b 100644 --- a/pkg/safe/routine.go +++ b/pkg/safe/routine.go @@ -155,8 +155,9 @@ func GoWithRecover(goroutine func(), customRecover func(err interface{})) { } func defaultRecoverGoroutine(err interface{}) { - log.Errorf("Error in Go routine: %s", err) - log.Errorf("Stack: %s", debug.Stack()) + logger := log.WithoutContext() + logger.Errorf("Error in Go routine: %s", err) + logger.Errorf("Stack: %s", debug.Stack()) } // OperationWithRecover wrap a backoff operation in a Recover diff --git a/pkg/server/cookie/cookie.go b/pkg/server/cookie/cookie.go index 632763a8f..5ac3339be 100644 --- a/pkg/server/cookie/cookie.go +++ b/pkg/server/cookie/cookie.go @@ -27,7 +27,7 @@ func GenerateName(backendName string) string { _, err := hash.Write(data) if err != nil { // Impossible case - log.Errorf("Fail to create cookie name: %v", err) + log.WithoutContext().Errorf("Fail to create cookie name: %v", err) } return fmt.Sprintf("_%x", hash.Sum(nil))[:cookieNameLength] diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 86fb882c6..d55aa98b0 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -203,6 +203,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceManager := tcp.NewManager(conf) tlsManager := tls.NewManager() tlsManager.UpdateConfigs( + context.Background(), map[string]tls.Store{}, map[string]tls.Options{ "default": { diff --git a/pkg/server/server.go b/pkg/server/server.go index 7a67e46f9..234d66e0d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -270,7 +270,8 @@ func (s *Server) listenProviders(stop chan bool) { if configMsg.Configuration != nil { s.preLoadConfiguration(configMsg) } else { - log.Debugf("Received nil configuration from provider %q, skipping.", configMsg.ProviderName) + log.WithoutContext().WithField(log.ProviderName, configMsg.ProviderName). + Debug("Received nil configuration from provider, skipping.") } } } @@ -285,18 +286,20 @@ func (s *Server) AddListener(listener func(dynamic.Configuration)) { } func (s *Server) startProvider() { + logger := log.WithoutContext() + jsonConf, err := json.Marshal(s.provider) if err != nil { - log.WithoutContext().Debugf("Unable to marshal provider configuration %T: %v", s.provider, err) + logger.Debugf("Unable to marshal provider configuration %T: %v", s.provider, err) } - log.WithoutContext().Infof("Starting provider %T %s", s.provider, jsonConf) + logger.Infof("Starting provider %T %s", s.provider, jsonConf) currentProvider := s.provider safe.Go(func() { err := currentProvider.Provide(s.configurationChan, s.routinesPool) if err != nil { - log.WithoutContext().Errorf("Error starting provider %T: %s", s.provider, err) + logger.Errorf("Error starting provider %T: %s", s.provider, err) } }) } diff --git a/pkg/server/server_configuration.go b/pkg/server/server_configuration.go index c4b703504..edeb0ede4 100644 --- a/pkg/server/server_configuration.go +++ b/pkg/server/server_configuration.go @@ -63,7 +63,7 @@ func (s *Server) loadConfiguration(configMsg dynamic.Message) { // loadConfigurationTCP returns a new gorilla.mux Route from the specified global configuration and the dynamic // provider configurations. func (s *Server) loadConfigurationTCP(configurations dynamic.Configurations) map[string]*tcpCore.Router { - ctx := context.TODO() + ctx := context.Background() var entryPoints []string for entryPointName := range s.entryPointsTCP { @@ -72,7 +72,7 @@ func (s *Server) loadConfigurationTCP(configurations dynamic.Configurations) map conf := mergeConfiguration(configurations) - s.tlsManager.UpdateConfigs(conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates) + s.tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates) rtConf := runtime.NewConfig(conf) handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, rtConf, entryPoints) diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index cad56b3ab..59db9ba85 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -72,14 +72,14 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint) (*T router := &tcp.Router{} - httpServer, err := createHTTPServer(listener, configuration, true) + httpServer, err := createHTTPServer(ctx, listener, configuration, true) if err != nil { return nil, fmt.Errorf("error preparing httpServer: %v", err) } router.HTTPForwarder(httpServer.Forwarder) - httpsServer, err := createHTTPServer(listener, configuration, false) + httpsServer, err := createHTTPServer(ctx, listener, configuration, false) if err != nil { return nil, fmt.Errorf("error preparing httpsServer: %v", err) } @@ -128,13 +128,13 @@ func writeCloser(conn net.Conn) (tcp.WriteCloser, error) { } func (e *TCPEntryPoint) startTCP(ctx context.Context) { - - log.FromContext(ctx).Debugf("Start TCP Server") + logger := log.FromContext(ctx) + logger.Debugf("Start TCP Server") for { conn, err := e.listener.Accept() if err != nil { - log.Error(err) + logger.Error(err) return } @@ -372,7 +372,7 @@ type httpServer struct { Switcher *middlewares.HTTPHandlerSwitcher } -func createHTTPServer(ln net.Listener, configuration *static.EntryPoint, withH2c bool) (*httpServer, error) { +func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool) (*httpServer, error) { httpSwitcher := middlewares.NewHandlerSwitcher(buildDefaultHTTPRouter()) var handler http.Handler @@ -398,7 +398,7 @@ func createHTTPServer(ln net.Listener, configuration *static.EntryPoint, withH2c go func() { err := serverHTTP.Serve(listener) if err != nil { - log.Errorf("Error while starting server: %v", err) + log.FromContext(ctx).Errorf("Error while starting server: %v", err) } }() return &httpServer{ diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 1181b2584..bb030889a 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -232,7 +232,7 @@ func (m *Manager) LaunchHealthCheck() { } // FIXME metrics and context - healthcheck.GetHealthCheck().SetBackendsConfiguration(context.TODO(), backendConfigs) + healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs) } func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler, backend string, hc *dynamic.HealthCheck) *healthcheck.Options { diff --git a/pkg/tcp/rr_load_balancer.go b/pkg/tcp/rr_load_balancer.go index 92be94f54..eb5773ae2 100644 --- a/pkg/tcp/rr_load_balancer.go +++ b/pkg/tcp/rr_load_balancer.go @@ -39,7 +39,7 @@ func (r *RRLoadBalancer) next() Handler { if r.current >= len(r.servers) { r.current = 0 - log.Debugf("Load balancer: going back to the first available server") + log.WithoutContext().Debugf("Load balancer: going back to the first available server") } handler := r.servers[r.current] diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go index 27fdbb1eb..ebdeb9e22 100644 --- a/pkg/tls/tlsmanager.go +++ b/pkg/tls/tlsmanager.go @@ -1,6 +1,7 @@ package tls import ( + "context" "crypto/tls" "crypto/x509" "errors" @@ -30,7 +31,7 @@ func NewManager() *Manager { } // UpdateConfigs updates the TLS* configuration options -func (m *Manager) UpdateConfigs(stores map[string]Store, configs map[string]Options, certs []*CertAndStores) { +func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, configs map[string]Options, certs []*CertAndStores) { m.lock.Lock() defer m.lock.Unlock() @@ -40,9 +41,10 @@ func (m *Manager) UpdateConfigs(stores map[string]Store, configs map[string]Opti m.stores = make(map[string]*CertificateStore) for storeName, storeConfig := range m.storesConfig { - store, err := buildCertificateStore(storeConfig) + ctxStore := log.With(ctx, log.Str(log.TLSStoreName, storeName)) + store, err := buildCertificateStore(ctxStore, storeConfig) if err != nil { - log.Errorf("Error while creating certificate store %s: %v", storeName, err) + log.FromContext(ctxStore).Errorf("Error while creating certificate store: %v", err) continue } m.stores[storeName] = store @@ -52,14 +54,15 @@ func (m *Manager) UpdateConfigs(stores map[string]Store, configs map[string]Opti for _, conf := range certs { if len(conf.Stores) == 0 { if log.GetLevel() >= logrus.DebugLevel { - log.Debugf("No store is defined to add the certificate %s, it will be added to the default store.", + log.FromContext(ctx).Debugf("No store is defined to add the certificate %s, it will be added to the default store.", conf.Certificate.GetTruncatedCertificateName()) } conf.Stores = []string{"default"} } for _, store := range conf.Stores { + ctxStore := log.With(ctx, log.Str(log.TLSStoreName, store)) if err := conf.Certificate.AppendCertificate(storesCertificates, store); err != nil { - log.Errorf("Unable to append certificate %s to store %s: %v", conf.Certificate.GetTruncatedCertificateName(), store, err) + log.FromContext(ctxStore).Errorf("Unable to append certificate %s to store: %v", conf.Certificate.GetTruncatedCertificateName(), err) } } } @@ -125,7 +128,7 @@ func (m *Manager) Get(storeName string, configName string) (*tls.Config, error) func (m *Manager) getStore(storeName string) *CertificateStore { _, ok := m.stores[storeName] if !ok { - m.stores[storeName], _ = buildCertificateStore(Store{}) + m.stores[storeName], _ = buildCertificateStore(context.Background(), Store{}) } return m.stores[storeName] } @@ -138,7 +141,7 @@ func (m *Manager) GetStore(storeName string) *CertificateStore { return m.getStore(storeName) } -func buildCertificateStore(tlsStore Store) (*CertificateStore, error) { +func buildCertificateStore(ctx context.Context, tlsStore Store) (*CertificateStore, error) { certificateStore := NewCertificateStore() certificateStore.DynamicCerts.Set(make(map[string]*tls.Certificate)) @@ -149,7 +152,7 @@ func buildCertificateStore(tlsStore Store) (*CertificateStore, error) { } certificateStore.DefaultCertificate = cert } else { - log.Debug("No default certificate, generating one") + log.FromContext(ctx).Debug("No default certificate, generating one") cert, err := generate.DefaultCertificate() if err != nil { return certificateStore, err diff --git a/pkg/tls/tlsmanager_test.go b/pkg/tls/tlsmanager_test.go index 9fcc4935b..06b364ea1 100644 --- a/pkg/tls/tlsmanager_test.go +++ b/pkg/tls/tlsmanager_test.go @@ -1,6 +1,7 @@ package tls import ( + "context" "crypto/tls" "crypto/x509" "encoding/pem" @@ -57,7 +58,7 @@ func TestTLSInStore(t *testing.T) { }} tlsManager := NewManager() - tlsManager.UpdateConfigs(nil, nil, dynamicConfigs) + tlsManager.UpdateConfigs(context.Background(), nil, nil, dynamicConfigs) certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*tls.Certificate) if len(certs) == 0 { @@ -74,14 +75,15 @@ func TestTLSInvalidStore(t *testing.T) { }} tlsManager := NewManager() - tlsManager.UpdateConfigs(map[string]Store{ - "default": { - DefaultCertificate: &Certificate{ - CertFile: "/wrong", - KeyFile: "/wrong", + tlsManager.UpdateConfigs(context.Background(), + map[string]Store{ + "default": { + DefaultCertificate: &Certificate{ + CertFile: "/wrong", + KeyFile: "/wrong", + }, }, - }, - }, nil, dynamicConfigs) + }, nil, dynamicConfigs) certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*tls.Certificate) if len(certs) == 0 { @@ -131,7 +133,7 @@ func TestManager_Get(t *testing.T) { } tlsManager := NewManager() - tlsManager.UpdateConfigs(nil, tlsConfigs, dynamicConfigs) + tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, dynamicConfigs) for _, test := range testCases { test := test @@ -270,7 +272,7 @@ func TestClientAuth(t *testing.T) { } tlsManager := NewManager() - tlsManager.UpdateConfigs(nil, tlsConfigs, nil) + tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, nil) for _, test := range testCases { test := test diff --git a/pkg/version/version.go b/pkg/version/version.go index c208f650e..7b5975bf1 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -43,7 +43,7 @@ func (v Handler) Append(router *mux.Router) { } if err := templatesRenderer.JSON(response, http.StatusOK, v); err != nil { - log.Error(err) + log.WithoutContext().Error(err) } }) } @@ -53,34 +53,39 @@ func CheckNewVersion() { if Version == "dev" { return } + + logger := log.WithoutContext() + client := github.NewClient(nil) + updateURL, err := url.Parse("https://update.traefik.io/") if err != nil { - log.Warnf("Error checking new version: %s", err) + logger.Warnf("Error checking new version: %s", err) return } client.BaseURL = updateURL + releases, resp, err := client.Repositories.ListReleases(context.Background(), "containous", "traefik", nil) if err != nil { - log.Warnf("Error checking new version: %s", err) + logger.Warnf("Error checking new version: %s", err) return } if resp.StatusCode != http.StatusOK { - log.Warnf("Error checking new version: status=%s", resp.Status) + logger.Warnf("Error checking new version: status=%s", resp.Status) return } currentVersion, err := goversion.NewVersion(Version) if err != nil { - log.Warnf("Error checking new version: %s", err) + logger.Warnf("Error checking new version: %s", err) return } for _, release := range releases { releaseVersion, err := goversion.NewVersion(*release.TagName) if err != nil { - log.Warnf("Error checking new version: %s", err) + logger.Warnf("Error checking new version: %s", err) return } @@ -89,7 +94,7 @@ func CheckNewVersion() { } if releaseVersion.GreaterThan(currentVersion) { - log.Warnf("A new release has been found: %s. Please consider updating.", releaseVersion.String()) + logger.Warnf("A new release has been found: %s. Please consider updating.", releaseVersion.String()) return } } From 685c6dc00c74cf6bcf74237314eae9ea7213a06f Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Fri, 13 Sep 2019 20:00:06 +0200 Subject: [PATCH 09/19] Add weighted round robin load balancer on TCP Co-authored-by: Mathieu Lonjaret --- docs/content/routing/services/index.md | 62 ++++++++- integration/fixtures/tcp/wrr.toml | 42 ++++++ integration/rest_test.go | 4 +- integration/tcp_test.go | 33 +++++ pkg/api/handler_overview_test.go | 6 +- pkg/api/handler_tcp_test.go | 28 ++-- pkg/api/handler_test.go | 2 +- pkg/config/dynamic/http_config.go | 2 +- pkg/config/dynamic/tcp_config.go | 34 ++++- pkg/config/dynamic/zz_generated.deepcopy.go | 103 ++++++++++---- pkg/config/label/label_test.go | 8 +- pkg/config/runtime/runtime_test.go | 8 +- pkg/provider/docker/config.go | 4 +- pkg/provider/docker/config_test.go | 12 +- .../fixtures/tcp/with_termination_delay.yml | 16 +++ .../crd/fixtures/tcp/with_two_services.yml | 3 +- .../kubernetes/crd/kubernetes_http.go | 2 + pkg/provider/kubernetes/crd/kubernetes_tcp.go | 99 ++++++++----- .../kubernetes/crd/kubernetes_test.go | 109 +++++++++++---- .../crd/traefik/v1alpha1/ingressroutetcp.go | 6 +- .../traefik/v1alpha1/zz_generated.deepcopy.go | 14 +- pkg/provider/marathon/config.go | 2 +- pkg/provider/marathon/config_test.go | 10 +- pkg/provider/rancher/config.go | 4 +- pkg/provider/rancher/config_test.go | 12 +- pkg/server/router/tcp/router_test.go | 8 +- pkg/server/service/service.go | 4 +- pkg/server/service/tcp/service.go | 65 ++++++--- pkg/server/service/tcp/service_test.go | 18 +-- pkg/tcp/proxy.go | 5 +- pkg/tcp/rr_load_balancer.go | 48 ------- pkg/tcp/wrr_load_balancer.go | 122 ++++++++++++++++ pkg/tcp/wrr_load_balancer_test.go | 131 ++++++++++++++++++ 33 files changed, 787 insertions(+), 239 deletions(-) create mode 100644 integration/fixtures/tcp/wrr.toml create mode 100644 pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml delete mode 100644 pkg/tcp/rr_load_balancer.go create mode 100644 pkg/tcp/wrr_load_balancer.go create mode 100644 pkg/tcp/wrr_load_balancer_test.go diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 0e9e109df..fb71898eb 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -404,13 +404,14 @@ http: ### General -Currently, `LoadBalancer` is the only supported kind of TCP `Service`. -However, since Traefik is an ever evolving project, other kind of TCP Services will be available in the future, -reason why you have to specify it. +Each of the fields of the service section represents a kind of service. +Which means, that for each specified service, one of the fields, and only one, +has to be enabled to define what kind of service is created. +Currently, the two available kinds are `LoadBalancer`, and `Weighted`. -### Load Balancer +### Servers Load Balancer -The load balancers are able to load balance the requests between multiple instances of your programs. +The servers load balancer is in charge of balancing the requests between the servers of the same service. ??? example "Declaring a Service with Two Servers -- Using the [File Provider](../../providers/file.md)" @@ -486,3 +487,54 @@ A negative value means an infinite deadline (i.e. the connection is never fully loadBalancer: terminationDelay: 200 ``` + +### Weighted + +The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the requests between multiple services based on provided weights. + +This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers). + +This strategy can only be defined with [File](../../providers/file.md). + +```toml tab="TOML" +[tcp.services] + [tcp.services.app] + [[tcp.services.app.weighted.services]] + name = "appv1" + weight = 3 + [[tcp.services.app.weighted.services]] + name = "appv2" + weight = 1 + + [tcp.services.appv1] + [tcp.services.appv1.loadBalancer] + [[tcp.services.appv1.loadBalancer.servers]] + address = "private-ip-server-1/:8080" + + [tcp.services.appv2] + [tcp.services.appv2.loadBalancer] + [[tcp.services.appv2.loadBalancer.servers]] + address = "private-ip-server-2/:8080" +``` + +```yaml tab="YAML" +tcp: + services: + app: + weighted: + services: + - name: appv1 + weight: 3 + - name: appv2 + weight: 1 + + appv1: + loadBalancer: + servers: + - address: "xxx.xxx.xxx.xxx:8080" + + appv2: + loadBalancer: + servers: + - address: "xxx.xxx.xxx.xxx:8080" +``` diff --git a/integration/fixtures/tcp/wrr.toml b/integration/fixtures/tcp/wrr.toml new file mode 100644 index 000000000..90cea1a00 --- /dev/null +++ b/integration/fixtures/tcp/wrr.toml @@ -0,0 +1,42 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.tcp] + address = ":8093" + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## +[tcp] + [tcp.routers] + [tcp.routers.to-whoami-a] + rule = "HostSNI(`whoami-a.test`)" + service = "whoami" + entryPoints = [ "tcp" ] + [tcp.routers.to-whoami-a.tls] + passthrough=true + + [[tcp.services.whoami.weighted.services]] + name="whoami-a" + weight=3 + [[tcp.services.whoami.weighted.services]] + name="whoami-b" + weight=1 + + [tcp.services.whoami-a.loadBalancer] + [[tcp.services.whoami-a.loadBalancer.servers]] + address = "localhost:8081" + + [tcp.services.whoami-b.loadBalancer] + [[tcp.services.whoami-b.loadBalancer.servers]] + address = "localhost:8082" + diff --git a/integration/rest_test.go b/integration/rest_test.go index d87092571..0b6fa731f 100644 --- a/integration/rest_test.go +++ b/integration/rest_test.go @@ -79,7 +79,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) { }, Services: map[string]*dynamic.TCPService{ "service1": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80", @@ -183,7 +183,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) { }, Services: map[string]*dynamic.TCPService{ "service1": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80", diff --git a/integration/tcp_test.go b/integration/tcp_test.go index c1fc0d57c..ba49b3fdb 100644 --- a/integration/tcp_test.go +++ b/integration/tcp_test.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httptest" "os" + "strings" "time" "github.com/containous/traefik/v2/integration/try" @@ -266,3 +267,35 @@ func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion return string(out[:n]), nil } + +func (s *TCPSuite) TestWRR(c *check.C) { + file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{}) + 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() + + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI")) + c.Assert(err, checker.IsNil) + + call := map[string]int{} + for i := 0; i < 4; i++ { + // Traefik passes through, termination handled by whoami-a + out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true) + c.Assert(err, checker.IsNil) + switch { + case strings.Contains(out, "whoami-a"): + call["whoami-a"]++ + case strings.Contains(out, "whoami-b"): + call["whoami-b"]++ + default: + call["unknown"]++ + } + } + + c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 3, "whoami-b": 1}) +} diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go index e8eaa7c92..10ff7f693 100644 --- a/pkg/api/handler_overview_test.go +++ b/pkg/api/handler_overview_test.go @@ -136,7 +136,7 @@ func TestHandler_Overview(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "tcpfoo-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", @@ -148,7 +148,7 @@ func TestHandler_Overview(t *testing.T) { }, "tcpbar-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2", @@ -160,7 +160,7 @@ func TestHandler_Overview(t *testing.T) { }, "tcpfii-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2", diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go index 798876de4..f033c0292 100644 --- a/pkg/api/handler_tcp_test.go +++ b/pkg/api/handler_tcp_test.go @@ -255,7 +255,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", @@ -268,7 +268,7 @@ func TestHandler_TCP(t *testing.T) { }, "baz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -281,7 +281,7 @@ func TestHandler_TCP(t *testing.T) { }, "foz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -307,7 +307,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", @@ -320,7 +320,7 @@ func TestHandler_TCP(t *testing.T) { }, "baz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -333,7 +333,7 @@ func TestHandler_TCP(t *testing.T) { }, "foz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -359,7 +359,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", @@ -372,7 +372,7 @@ func TestHandler_TCP(t *testing.T) { }, "baz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -385,7 +385,7 @@ func TestHandler_TCP(t *testing.T) { }, "foz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -411,7 +411,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", @@ -423,7 +423,7 @@ func TestHandler_TCP(t *testing.T) { }, "baz@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.2:2345", @@ -435,7 +435,7 @@ func TestHandler_TCP(t *testing.T) { }, "test@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.3:2345", @@ -459,7 +459,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", @@ -483,7 +483,7 @@ func TestHandler_TCP(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "bar@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:2345", diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go index 7b506cd6c..3fe6d3649 100644 --- a/pkg/api/handler_test.go +++ b/pkg/api/handler_test.go @@ -91,7 +91,7 @@ func TestHandler_RawData(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "tcpfoo-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 7fc264018..ab56419bd 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -77,7 +77,7 @@ type WRRService struct { Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"` } -// SetDefaults Default values for a ServersLoadBalancer. +// SetDefaults Default values for a WRRService. func (w *WRRService) SetDefaults() { defaultWeight := 1 w.Weight = &defaultWeight diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index d8ff19cb6..272da7b14 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -18,7 +18,29 @@ type TCPConfiguration struct { // TCPService holds a tcp service configuration (can only be of one type at the same time). type TCPService struct { - LoadBalancer *TCPLoadBalancerService `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` + LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` + Weighted *TCPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"` +} + +// +k8s:deepcopy-gen=true + +// TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services. +type TCPWeightedRoundRobin struct { + Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` +} + +// +k8s:deepcopy-gen=true + +// TCPWRRService is a reference to a tcp service load-balanced with weighted round robin. +type TCPWRRService struct { + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` + Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"` +} + +// SetDefaults Default values for a TCPWRRService. +func (w *TCPWRRService) SetDefaults() { + defaultWeight := 1 + w.Weight = &defaultWeight } // +k8s:deepcopy-gen=true @@ -43,8 +65,8 @@ type RouterTCPTLSConfig struct { // +k8s:deepcopy-gen=true -// TCPLoadBalancerService holds the LoadBalancerService configuration. -type TCPLoadBalancerService struct { +// TCPServersLoadBalancer holds the LoadBalancerService configuration. +type TCPServersLoadBalancer struct { // TerminationDelay, corresponds to the deadline that the proxy sets, after one // of its connected peers indicates it has closed the writing capability of its // connection, to close the reading capability as well, hence fully terminating the @@ -54,14 +76,14 @@ type TCPLoadBalancerService struct { Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` } -// SetDefaults Default values for a TCPLoadBalancerService -func (l *TCPLoadBalancerService) SetDefaults() { +// SetDefaults Default values for a TCPServersLoadBalancer +func (l *TCPServersLoadBalancer) SetDefaults() { defaultTerminationDelay := 100 // in milliseconds l.TerminationDelay = &defaultTerminationDelay } // Mergeable tells if the given service is mergeable. -func (l *TCPLoadBalancerService) Mergeable(loadBalancer *TCPLoadBalancerService) bool { +func (l *TCPServersLoadBalancer) Mergeable(loadBalancer *TCPServersLoadBalancer) bool { savedServers := l.Servers defer func() { l.Servers = savedServers diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 25d9d4807..36635949d 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -1152,32 +1152,6 @@ func (in *TCPConfiguration) DeepCopy() *TCPConfiguration { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TCPLoadBalancerService) DeepCopyInto(out *TCPLoadBalancerService) { - *out = *in - if in.TerminationDelay != nil { - in, out := &in.TerminationDelay, &out.TerminationDelay - *out = new(int) - **out = **in - } - if in.Servers != nil { - in, out := &in.Servers, &out.Servers - *out = make([]TCPServer, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPLoadBalancerService. -func (in *TCPLoadBalancerService) DeepCopy() *TCPLoadBalancerService { - if in == nil { - return nil - } - out := new(TCPLoadBalancerService) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPRouter) DeepCopyInto(out *TCPRouter) { *out = *in @@ -1220,12 +1194,43 @@ func (in *TCPServer) DeepCopy() *TCPServer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) { + *out = *in + if in.TerminationDelay != nil { + in, out := &in.TerminationDelay, &out.TerminationDelay + *out = new(int) + **out = **in + } + if in.Servers != nil { + in, out := &in.Servers, &out.Servers + *out = make([]TCPServer, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPServersLoadBalancer. +func (in *TCPServersLoadBalancer) DeepCopy() *TCPServersLoadBalancer { + if in == nil { + return nil + } + out := new(TCPServersLoadBalancer) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPService) DeepCopyInto(out *TCPService) { *out = *in if in.LoadBalancer != nil { in, out := &in.LoadBalancer, &out.LoadBalancer - *out = new(TCPLoadBalancerService) + *out = new(TCPServersLoadBalancer) + (*in).DeepCopyInto(*out) + } + if in.Weighted != nil { + in, out := &in.Weighted, &out.Weighted + *out = new(TCPWeightedRoundRobin) (*in).DeepCopyInto(*out) } return @@ -1241,6 +1246,50 @@ func (in *TCPService) DeepCopy() *TCPService { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPWRRService) DeepCopyInto(out *TCPWRRService) { + *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWRRService. +func (in *TCPWRRService) DeepCopy() *TCPWRRService { + if in == nil { + return nil + } + out := new(TCPWRRService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPWeightedRoundRobin) DeepCopyInto(out *TCPWeightedRoundRobin) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]TCPWRRService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWeightedRoundRobin. +func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin { + if in == nil { + return nil + } + out := new(TCPWeightedRoundRobin) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) { *out = *in diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 989eb6be6..a88cb588d 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -208,7 +208,7 @@ func TestDecodeConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "Service0": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Port: "42", @@ -218,7 +218,7 @@ func TestDecodeConfiguration(t *testing.T) { }, }, "Service1": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Port: "42", @@ -614,7 +614,7 @@ func TestEncodeConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "Service0": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Port: "42", @@ -623,7 +623,7 @@ func TestEncodeConfiguration(t *testing.T) { }, }, "Service1": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Port: "42", diff --git a/pkg/config/runtime/runtime_test.go b/pkg/config/runtime/runtime_test.go index b2cbc6238..5ff0a97d6 100644 --- a/pkg/config/runtime/runtime_test.go +++ b/pkg/config/runtime/runtime_test.go @@ -488,7 +488,7 @@ func TestPopulateUsedBy(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "foo-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", @@ -522,7 +522,7 @@ func TestPopulateUsedBy(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "foo-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", @@ -598,7 +598,7 @@ func TestPopulateUsedBy(t *testing.T) { TCPServices: map[string]*runtime.TCPServiceInfo{ "foo-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", @@ -614,7 +614,7 @@ func TestPopulateUsedBy(t *testing.T) { }, "bar-service@myprovider": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1", diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 9c3c18bf0..b0aeb4c5f 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -78,7 +78,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d if len(configuration.Services) == 0 { configuration.Services = make(map[string]*dynamic.TCPService) - lb := &dynamic.TCPLoadBalancerService{} + lb := &dynamic.TCPServersLoadBalancer{} lb.SetDefaults() configuration.Services[serviceName] = &dynamic.TCPService{ LoadBalancer: lb, @@ -145,7 +145,7 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool return true } -func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPLoadBalancerService) error { +func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPServersLoadBalancer) error { serverPort := "" if loadBalancer != nil && len(loadBalancer.Servers) > 0 { serverPort = loadBalancer.Servers[0].Port diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index 2d927c0d7..dcaa20d4a 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -2088,7 +2088,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "Test": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -2133,7 +2133,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "Test": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -2188,7 +2188,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -2264,7 +2264,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -2331,7 +2331,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -2377,7 +2377,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml new file mode 100644 index 000000000..1d942ab6a --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml @@ -0,0 +1,16 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRouteTCP +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: HostSNI(`foo.com`) + services: + - name: whoamitcp + port: 8000 + terminationDelay: 500 diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml index bc846568e..73f60f037 100644 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml @@ -13,6 +13,7 @@ spec: services: - name: whoamitcp port: 8000 + weight: 2 - name: whoamitcp2 port: 8080 - + weight: 3 diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 5144d330f..b6835bce1 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -73,6 +73,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli continue } + // If there is only one service defined, we skip the creation of the load balancer of services, + // i.e. the service on top is directly a load balancer of servers. if len(route.Services) == 1 { conf.Services[serviceName] = balancerServerHTTP break diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 33744a95b..a5a6b74e8 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -49,9 +49,16 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client continue } - var allServers []dynamic.TCPServer + key, err := makeServiceKey(route.Match, ingressName) + if err != nil { + logger.Error(err) + continue + } + + serviceName := makeID(ingressRouteTCP.Namespace, key) + for _, service := range route.Services { - servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service) + balancerServerTCP, err := createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service) if err != nil { logger. WithField("serviceName", service.Name). @@ -60,16 +67,28 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client continue } - allServers = append(allServers, servers...) + // If there is only one service defined, we skip the creation of the load balancer of services, + // i.e. the service on top is directly a load balancer of servers. + if len(route.Services) == 1 { + conf.Services[serviceName] = balancerServerTCP + break + } + + serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port) + conf.Services[serviceKey] = balancerServerTCP + + srv := dynamic.TCPWRRService{Name: serviceKey} + srv.SetDefaults() + if service.Weight != nil { + srv.Weight = service.Weight + } + + if conf.Services[serviceName] == nil { + conf.Services[serviceName] = &dynamic.TCPService{Weighted: &dynamic.TCPWeightedRoundRobin{}} + } + conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv) } - key, e := makeServiceKey(route.Match, ingressName) - if e != nil { - logger.Error(e) - continue - } - - serviceName := makeID(ingressRouteTCP.Namespace, key) conf.Routers[serviceName] = &dynamic.TCPRouter{ EntryPoints: ingressRouteTCP.Spec.EntryPoints, Rule: route.Match, @@ -83,37 +102,53 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client Domains: ingressRouteTCP.Spec.TLS.Domains, } - if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 { - tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name - // Is a Kubernetes CRD reference (i.e. not a cross-provider reference) - ns := ingressRouteTCP.Spec.TLS.Options.Namespace - if !strings.Contains(tlsOptionsName, "@") { - if len(ns) == 0 { - ns = ingressRouteTCP.Namespace - } - tlsOptionsName = makeID(ns, tlsOptionsName) - } else if len(ns) > 0 { - logger. - WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name). - Warnf("namespace %q is ignored in cross-provider context", ns) - } - - conf.Routers[serviceName].TLS.Options = tlsOptionsName - + if ingressRouteTCP.Spec.TLS.Options == nil || len(ingressRouteTCP.Spec.TLS.Options.Name) == 0 { + continue } + + tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name + // Is a Kubernetes CRD reference (i.e. not a cross-provider reference) + ns := ingressRouteTCP.Spec.TLS.Options.Namespace + if !strings.Contains(tlsOptionsName, "@") { + if len(ns) == 0 { + ns = ingressRouteTCP.Namespace + } + tlsOptionsName = makeID(ns, tlsOptionsName) + } else if len(ns) > 0 { + logger. + WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name). + Warnf("namespace %q is ignored in cross-provider context", ns) + } + + conf.Routers[serviceName].TLS.Options = tlsOptionsName + } - conf.Services[serviceName] = &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ - Servers: allServers, - }, - } } } return conf } +func createLoadBalancerServerTCP(client Client, namespace string, service v1alpha1.ServiceTCP) (*dynamic.TCPService, error) { + servers, err := loadTCPServers(client, namespace, service) + if err != nil { + return nil, err + } + + tcpService := &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: servers, + }, + } + + if service.TerminationDelay != nil { + tcpService.LoadBalancer.TerminationDelay = service.TerminationDelay + } + + return tcpService, nil +} + func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) { service, exists, err := client.GetService(namespace, svc.Name) if err != nil { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 03ceb3de2..dbdb7ef85 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -55,7 +55,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -92,7 +92,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -106,7 +106,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, "default/test.route-f44ce589164e656d231c": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -130,7 +130,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, { - desc: "One ingress Route with two different services, their servers will merge", + desc: "One ingress Route with two different services", paths: []string{"tcp/services.yml", "tcp/with_two_services.yml"}, expected: &dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ @@ -143,27 +143,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ - Servers: []dynamic.TCPServer{ + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{ { - Address: "10.10.0.1:8000", - Port: "", + Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000", + Weight: func(i int) *int { return &i }(2), }, { - Address: "10.10.0.2:8000", - Port: "", - }, - { - Address: "10.10.0.3:8080", - Port: "", - }, - { - Address: "10.10.0.4:8080", - Port: "", + Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080", + Weight: func(i int) *int { return &i }(3), }, }, }, - }}, + }, + "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.1:8000", + }, + { + Address: "10.10.0.2:8000", + }, + }, + }, + }, + "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.3:8080", + }, + { + Address: "10.10.0.4:8080", + }, + }, + }, + }, + }, }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{}, @@ -247,7 +264,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -286,7 +303,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -345,7 +362,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -403,7 +420,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -460,7 +477,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -506,7 +523,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -552,7 +569,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -589,7 +606,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "default/test.route-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "10.10.0.1:8000", @@ -612,6 +629,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "TCP with terminationDelay", + paths: []string{"tcp/services.yml", "tcp/with_termination_delay.yml"}, + expected: &dynamic.Configuration{ + TLS: &dynamic.TLSConfiguration{}, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "default/test.route-fdd3e9338e47a45efefc": { + EntryPoints: []string{"foo"}, + Service: "default/test.route-fdd3e9338e47a45efefc", + Rule: "HostSNI(`foo.com`)", + }, + }, + Services: map[string]*dynamic.TCPService{ + "default/test.route-fdd3e9338e47a45efefc": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.1:8000", + Port: "", + }, + { + Address: "10.10.0.2:8000", + Port: "", + }, + }, + TerminationDelay: Int(500), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, } for _, test := range testCases { diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go index dde55c694..72c9a7d0e 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go @@ -44,8 +44,10 @@ type TLSOptionTCPRef struct { // ServiceTCP defines an upstream to proxy traffic. type ServiceTCP struct { - Name string `json:"name"` - Port int32 `json:"port"` + Name string `json:"name"` + Port int32 `json:"port"` + Weight *int `json:"weight,omitempty"` + TerminationDelay *int `json:"terminationDelay,omitempty"` } // +genclient diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go index 85d073da4..3f6770cbe 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go @@ -612,7 +612,9 @@ func (in *RouteTCP) DeepCopyInto(out *RouteTCP) { if in.Services != nil { in, out := &in.Services, &out.Services *out = make([]ServiceTCP, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } @@ -666,6 +668,16 @@ func (in *Service) DeepCopy() *Service { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) { *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int) + **out = **in + } + if in.TerminationDelay != nil { + in, out := &in.TerminationDelay, &out.TerminationDelay + *out = new(int) + **out = **in + } return } diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go index 08a767af4..e2f81c163 100644 --- a/pkg/provider/marathon/config.go +++ b/pkg/provider/marathon/config.go @@ -140,7 +140,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho if len(conf.Services) == 0 { conf.Services = make(map[string]*dynamic.TCPService) - lb := &dynamic.TCPLoadBalancerService{} + lb := &dynamic.TCPServersLoadBalancer{} lb.SetDefaults() conf.Services[appName] = &dynamic.TCPService{ LoadBalancer: lb, diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go index c2dc92b30..83c25736c 100644 --- a/pkg/provider/marathon/config_test.go +++ b/pkg/provider/marathon/config_test.go @@ -1236,7 +1236,7 @@ func TestBuildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "app": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "localhost:80", @@ -1268,7 +1268,7 @@ func TestBuildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "app": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "localhost:80", @@ -1308,7 +1308,7 @@ func TestBuildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "localhost:8080", @@ -1349,7 +1349,7 @@ func TestBuildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "localhost:8080", @@ -1390,7 +1390,7 @@ func TestBuildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "localhost:8080", diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go index e2787d96d..b6bfa9ad8 100644 --- a/pkg/provider/rancher/config.go +++ b/pkg/provider/rancher/config.go @@ -74,7 +74,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, service ran if len(configuration.Services) == 0 { configuration.Services = make(map[string]*dynamic.TCPService) - lb := &dynamic.TCPLoadBalancerService{} + lb := &dynamic.TCPServersLoadBalancer{} lb.SetDefaults() configuration.Services[serviceName] = &dynamic.TCPService{ LoadBalancer: lb, @@ -146,7 +146,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool { return true } -func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPLoadBalancerService) error { +func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error { log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name) serverPort := "" diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go index 14d172f35..0e771b190 100644 --- a/pkg/provider/rancher/config_test.go +++ b/pkg/provider/rancher/config_test.go @@ -508,7 +508,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "Test": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -545,7 +545,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "Test": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -588,7 +588,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -634,7 +634,7 @@ func Test_buildConfiguration(t *testing.T) { }, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -693,7 +693,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", @@ -731,7 +731,7 @@ func Test_buildConfiguration(t *testing.T) { Routers: map[string]*dynamic.TCPRouter{}, Services: map[string]*dynamic.TCPService{ "foo": { - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:8080", diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index d55aa98b0..935bbffe4 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -23,7 +23,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*runtime.TCPServiceInfo{ "foo-service": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Port: "8085", @@ -70,7 +70,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*runtime.TCPServiceInfo{ "foo-service": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -104,7 +104,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*runtime.TCPServiceInfo{ "foo-service": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", @@ -137,7 +137,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceConfig: map[string]*runtime.TCPServiceInfo{ "foo-service": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "127.0.0.1:80", diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index bb030889a..b153f30ac 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -93,7 +93,9 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons } } if count > 1 { - return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead") + err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead") + conf.AddError(err, true) + return nil, err } var lb http.Handler diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go index 2495e2551..ca52ad72a 100644 --- a/pkg/server/service/tcp/service.go +++ b/pkg/server/service/tcp/service.go @@ -2,6 +2,7 @@ package tcp import ( "context" + "errors" "fmt" "net" "time" @@ -30,41 +31,59 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName) ctx = log.With(ctx, log.Str(log.ServiceName, serviceName)) - // FIXME Check if the service is declared multiple times with different types conf, ok := m.configs[serviceQualifiedName] if !ok { return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName) } - if conf.LoadBalancer == nil { - err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName) + + if conf.LoadBalancer != nil && conf.Weighted != nil { + err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead") conf.AddError(err, true) return nil, err } logger := log.FromContext(ctx) + switch { + case conf.LoadBalancer != nil: + loadBalancer := tcp.NewWRRLoadBalancer() - loadBalancer := tcp.NewRRLoadBalancer() - - if conf.LoadBalancer.TerminationDelay == nil { - defaultTerminationDelay := 100 - conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay - } - duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay) - - for name, server := range conf.LoadBalancer.Servers { - if _, _, err := net.SplitHostPort(server.Address); err != nil { - logger.Errorf("In service %q: %v", serviceQualifiedName, err) - continue + if conf.LoadBalancer.TerminationDelay == nil { + defaultTerminationDelay := 100 + conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay } + duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay) - handler, err := tcp.NewProxy(server.Address, duration) - if err != nil { - logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err) - continue + for name, server := range conf.LoadBalancer.Servers { + if _, _, err := net.SplitHostPort(server.Address); err != nil { + logger.Errorf("In service %q: %v", serviceQualifiedName, err) + continue + } + + handler, err := tcp.NewProxy(server.Address, duration) + if err != nil { + logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err) + continue + } + + loadBalancer.AddServer(handler) + logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address) } - - loadBalancer.AddServer(handler) - logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address) + return loadBalancer, nil + case conf.Weighted != nil: + loadBalancer := tcp.NewWRRLoadBalancer() + for _, service := range conf.Weighted.Services { + handler, err := m.BuildTCP(rootCtx, service.Name) + if err != nil { + logger.Errorf("In service %q: %v", serviceQualifiedName, err) + return nil, err + } + loadBalancer.AddWeightServer(handler, service.Weight) + } + return loadBalancer, nil + default: + err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName) + conf.AddError(err, true) + return nil, err } - return loadBalancer, nil + } diff --git a/pkg/server/service/tcp/service_test.go b/pkg/server/service/tcp/service_test.go index 8d680f11f..495602f08 100644 --- a/pkg/server/service/tcp/service_test.go +++ b/pkg/server/service/tcp/service_test.go @@ -41,7 +41,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "test": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ {Address: "test:31"}, }, @@ -56,7 +56,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "test": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ {Address: "foobar"}, }, @@ -71,7 +71,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{}, + LoadBalancer: &dynamic.TCPServersLoadBalancer{}, }, }, }, @@ -82,7 +82,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{}, + LoadBalancer: &dynamic.TCPServersLoadBalancer{}, }, }, }, @@ -93,7 +93,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{}, + LoadBalancer: &dynamic.TCPServersLoadBalancer{}, }, }, }, @@ -105,7 +105,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "foobar.com:80", @@ -123,7 +123,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "192.168.0.12:80", @@ -141,7 +141,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "foobar.com", @@ -159,7 +159,7 @@ func TestManager_BuildTCP(t *testing.T) { configs: map[string]*runtime.TCPServiceInfo{ "serviceName@provider-1": { TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPLoadBalancerService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { Address: "192.168.0.12", diff --git a/pkg/tcp/proxy.go b/pkg/tcp/proxy.go index 2e7934415..0764fea5c 100644 --- a/pkg/tcp/proxy.go +++ b/pkg/tcp/proxy.go @@ -58,13 +58,14 @@ func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) { errClose := dst.CloseWrite() if errClose != nil { - log.WithoutContext().Errorf("Error while terminating connection: %v", errClose) + log.WithoutContext().Debugf("Error while terminating connection: %v", errClose) + return } if p.terminationDelay >= 0 { err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay)) if err != nil { - log.WithoutContext().Errorf("Error while setting deadline: %v", err) + log.WithoutContext().Debugf("Error while setting deadline: %v", err) } } } diff --git a/pkg/tcp/rr_load_balancer.go b/pkg/tcp/rr_load_balancer.go deleted file mode 100644 index eb5773ae2..000000000 --- a/pkg/tcp/rr_load_balancer.go +++ /dev/null @@ -1,48 +0,0 @@ -package tcp - -import ( - "sync" - - "github.com/containous/traefik/v2/pkg/log" -) - -// RRLoadBalancer is a naive RoundRobin load balancer for TCP services -type RRLoadBalancer struct { - servers []Handler - lock sync.RWMutex - current int -} - -// NewRRLoadBalancer creates a new RRLoadBalancer -func NewRRLoadBalancer() *RRLoadBalancer { - return &RRLoadBalancer{} -} - -// ServeTCP forwards the connection to the right service -func (r *RRLoadBalancer) ServeTCP(conn WriteCloser) { - if len(r.servers) == 0 { - log.WithoutContext().Error("no available server") - return - } - - r.next().ServeTCP(conn) -} - -// AddServer appends a server to the existing list -func (r *RRLoadBalancer) AddServer(server Handler) { - r.servers = append(r.servers, server) -} - -func (r *RRLoadBalancer) next() Handler { - r.lock.Lock() - defer r.lock.Unlock() - - if r.current >= len(r.servers) { - r.current = 0 - log.WithoutContext().Debugf("Load balancer: going back to the first available server") - } - - handler := r.servers[r.current] - r.current++ - return handler -} diff --git a/pkg/tcp/wrr_load_balancer.go b/pkg/tcp/wrr_load_balancer.go new file mode 100644 index 000000000..bfa69d924 --- /dev/null +++ b/pkg/tcp/wrr_load_balancer.go @@ -0,0 +1,122 @@ +package tcp + +import ( + "fmt" + "sync" + + "github.com/containous/traefik/v2/pkg/log" +) + +type server struct { + Handler + weight int +} + +// WRRLoadBalancer is a naive RoundRobin load balancer for TCP services +type WRRLoadBalancer struct { + servers []server + lock sync.RWMutex + currentWeight int + index int +} + +// NewWRRLoadBalancer creates a new WRRLoadBalancer +func NewWRRLoadBalancer() *WRRLoadBalancer { + return &WRRLoadBalancer{ + index: -1, + } +} + +// ServeTCP forwards the connection to the right service +func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) { + if len(b.servers) == 0 { + log.WithoutContext().Error("no available server") + return + } + + next, err := b.next() + if err != nil { + log.WithoutContext().Errorf("Error during load balancing: %v", err) + conn.Close() + } + next.ServeTCP(conn) +} + +// AddServer appends a server to the existing list +func (b *WRRLoadBalancer) AddServer(serverHandler Handler) { + w := 1 + b.AddWeightServer(serverHandler, &w) +} + +// AddWeightServer appends a server to the existing list with a weight +func (b *WRRLoadBalancer) AddWeightServer(serverHandler Handler, weight *int) { + w := 1 + if weight != nil { + w = *weight + } + b.servers = append(b.servers, server{Handler: serverHandler, weight: w}) +} + +func (b *WRRLoadBalancer) maxWeight() int { + max := -1 + for _, s := range b.servers { + if s.weight > max { + max = s.weight + } + } + return max +} + +func (b *WRRLoadBalancer) weightGcd() int { + divisor := -1 + for _, s := range b.servers { + if divisor == -1 { + divisor = s.weight + } else { + divisor = gcd(divisor, s.weight) + } + } + return divisor +} + +func gcd(a, b int) int { + for b != 0 { + a, b = b, a%b + } + return a +} + +func (b *WRRLoadBalancer) next() (Handler, error) { + b.lock.Lock() + defer b.lock.Unlock() + + if len(b.servers) == 0 { + return nil, fmt.Errorf("no servers in the pool") + } + + // The algo below may look messy, but is actually very simple + // it calculates the GCD and subtracts it on every iteration, what interleaves servers + // and allows us not to build an iterator every time we readjust weights + + // GCD across all enabled servers + gcd := b.weightGcd() + // Maximum weight across all enabled servers + max := b.maxWeight() + + for { + b.index = (b.index + 1) % len(b.servers) + if b.index == 0 { + b.currentWeight -= gcd + if b.currentWeight <= 0 { + b.currentWeight = max + if b.currentWeight == 0 { + return nil, fmt.Errorf("all servers have 0 weight") + } + } + } + srv := b.servers[b.index] + if srv.weight >= b.currentWeight { + return srv, nil + } + } +} diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go new file mode 100644 index 000000000..933f48976 --- /dev/null +++ b/pkg/tcp/wrr_load_balancer_test.go @@ -0,0 +1,131 @@ +package tcp + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fakeConn struct { + call map[string]int +} + +func (f *fakeConn) Read(b []byte) (n int, err error) { + panic("implement me") +} + +func (f *fakeConn) Write(b []byte) (n int, err error) { + f.call[string(b)]++ + return len(b), nil +} + +func (f *fakeConn) Close() error { + panic("implement me") +} + +func (f *fakeConn) LocalAddr() net.Addr { + panic("implement me") +} + +func (f *fakeConn) RemoteAddr() net.Addr { + panic("implement me") +} + +func (f *fakeConn) SetDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) SetReadDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) SetWriteDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) CloseWrite() error { + panic("implement me") +} + +func TestLoadBalancing(t *testing.T) { + testCases := []struct { + desc string + serversWeight map[string]int + totalCall int + expected map[string]int + }{ + { + desc: "RoundRobin", + serversWeight: map[string]int{ + "h1": 1, + "h2": 1, + }, + totalCall: 4, + expected: map[string]int{ + "h1": 2, + "h2": 2, + }, + }, + { + desc: "WeighedRoundRobin", + serversWeight: map[string]int{ + "h1": 3, + "h2": 1, + }, + totalCall: 4, + expected: map[string]int{ + "h1": 3, + "h2": 1, + }, + }, + { + desc: "WeighedRoundRobin with more call", + serversWeight: map[string]int{ + "h1": 3, + "h2": 1, + }, + totalCall: 16, + expected: map[string]int{ + "h1": 12, + "h2": 4, + }, + }, + { + desc: "WeighedRoundRobin with 0 weight server", + serversWeight: map[string]int{ + "h1": 3, + "h2": 0, + }, + totalCall: 16, + expected: map[string]int{ + "h1": 16, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + balancer := NewWRRLoadBalancer() + for server, weight := range test.serversWeight { + server := server + balancer.AddWeightServer(HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte(server)) + require.NoError(t, err) + }), &weight) + } + + conn := &fakeConn{call: make(map[string]int)} + for i := 0; i < test.totalCall; i++ { + balancer.ServeTCP(conn) + } + + assert.Equal(t, test.expected, conn.call) + }) + } +} From 255e88fbf6831be33f3d5e2ebcb2f3aa36bb2b3c Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 13 Sep 2019 20:44:04 +0200 Subject: [PATCH 10/19] Fix kubernetes id name --- integration/testdata/rawdata-crd.json | 38 +-- integration/testdata/rawdata-ingress.json | 20 +- pkg/provider/kubernetes/crd/kubernetes.go | 2 +- .../kubernetes/crd/kubernetes_test.go | 268 +++++++++--------- pkg/provider/kubernetes/ingress/kubernetes.go | 12 +- .../kubernetes/ingress/kubernetes_test.go | 166 +++++------ 6 files changed, 255 insertions(+), 251 deletions(-) diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 98ccb952e..b3e431820 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -1,28 +1,28 @@ { "routers": { - "default/test.route-6b204d94623b3df4370c@kubernetescrd": { + "default-test.route-6b204d94623b3df4370c@kubernetescrd": { "entryPoints": [ "web" ], - "service": "default/test.route-6b204d94623b3df4370c", + "service": "default-test.route-6b204d94623b3df4370c", "rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/bar`)", "priority": 12, "tls": { - "options": "default/mytlsoption" + "options": "default-mytlsoption" }, "status": "enabled", "using": [ "web" ] }, - "default/test2.route-23c7f4c450289ee29016@kubernetescrd": { + "default-test2.route-23c7f4c450289ee29016@kubernetescrd": { "entryPoints": [ "web" ], "middlewares": [ - "default/mychain@kubernetescrd" + "default-mychain@kubernetescrd" ], - "service": "default/test2.route-23c7f4c450289ee29016", + "service": "default-test2.route-23c7f4c450289ee29016", "rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/tobestripped`)", "status": "enabled", "using": [ @@ -31,18 +31,18 @@ } }, "middlewares": { - "default/mychain@kubernetescrd": { + "default-mychain@kubernetescrd": { "chain": { "middlewares": [ - "default/stripprefix@kubernetescrd" + "default-stripprefix@kubernetescrd" ] }, "status": "enabled", "usedBy": [ - "default/test2.route-23c7f4c450289ee29016@kubernetescrd" + "default-test2.route-23c7f4c450289ee29016@kubernetescrd" ] }, - "default/stripprefix@kubernetescrd": { + "default-stripprefix@kubernetescrd": { "stripPrefix": { "prefixes": [ "/tobestripped" @@ -52,7 +52,7 @@ } }, "services": { - "default/test.route-6b204d94623b3df4370c@kubernetescrd": { + "default-test.route-6b204d94623b3df4370c@kubernetescrd": { "loadBalancer": { "servers": [ { @@ -66,14 +66,14 @@ }, "status": "enabled", "usedBy": [ - "default/test.route-6b204d94623b3df4370c@kubernetescrd" + "default-test.route-6b204d94623b3df4370c@kubernetescrd" ], "serverStatus": { "http://10.42.0.2:80": "UP", "http://10.42.0.6:80": "UP" } }, - "default/test2.route-23c7f4c450289ee29016@kubernetescrd": { + "default-test2.route-23c7f4c450289ee29016@kubernetescrd": { "loadBalancer": { "servers": [ { @@ -87,7 +87,7 @@ }, "status": "enabled", "usedBy": [ - "default/test2.route-23c7f4c450289ee29016@kubernetescrd" + "default-test2.route-23c7f4c450289ee29016@kubernetescrd" ], "serverStatus": { "http://10.42.0.2:80": "UP", @@ -96,15 +96,15 @@ } }, "tcpRouters": { - "default/test3.route-673acf455cb2dab0b43a@kubernetescrd": { + "default-test3.route-673acf455cb2dab0b43a@kubernetescrd": { "entryPoints": [ "footcp" ], - "service": "default/test3.route-673acf455cb2dab0b43a", + "service": "default-test3.route-673acf455cb2dab0b43a", "rule": "HostSNI(`*`)", "tls": { "passthrough": false, - "options": "default/mytlsoption" + "options": "default-mytlsoption" }, "status": "enabled", "using": [ @@ -113,7 +113,7 @@ } }, "tcpServices": { - "default/test3.route-673acf455cb2dab0b43a@kubernetescrd": { + "default-test3.route-673acf455cb2dab0b43a@kubernetescrd": { "loadBalancer": { "terminationDelay": 100, "servers": [ @@ -127,7 +127,7 @@ }, "status": "enabled", "usedBy": [ - "default/test3.route-673acf455cb2dab0b43a@kubernetescrd" + "default-test3.route-673acf455cb2dab0b43a@kubernetescrd" ] } } diff --git a/integration/testdata/rawdata-ingress.json b/integration/testdata/rawdata-ingress.json index 4908d6388..422bde9a6 100644 --- a/integration/testdata/rawdata-ingress.json +++ b/integration/testdata/rawdata-ingress.json @@ -1,7 +1,7 @@ { "routers": { - "whoami-test-https/whoami-tls@kubernetes": { - "service": "default/whoami/http", + "whoami-test-https-whoami-tls@kubernetes": { + "service": "default-whoami-http", "rule": "Host(`whoami.test.https`) \u0026\u0026 PathPrefix(`/whoami`)", "tls": {}, "status": "enabled", @@ -10,8 +10,8 @@ "web" ] }, - "whoami-test-https/whoami@kubernetes": { - "service": "default/whoami/http", + "whoami-test-https-whoami@kubernetes": { + "service": "default-whoami-http", "rule": "Host(`whoami.test.https`) \u0026\u0026 PathPrefix(`/whoami`)", "status": "enabled", "using": [ @@ -19,8 +19,8 @@ "web" ] }, - "whoami-test/whoami@kubernetes": { - "service": "default/whoami/http", + "whoami-test-whoami@kubernetes": { + "service": "default-whoami-http", "rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)", "status": "enabled", "using": [ @@ -30,7 +30,7 @@ } }, "services": { - "default/whoami/http@kubernetes": { + "default-whoami-http@kubernetes": { "loadBalancer": { "servers": [ { @@ -44,9 +44,9 @@ }, "status": "enabled", "usedBy": [ - "whoami-test-https/whoami-tls@kubernetes", - "whoami-test-https/whoami@kubernetes", - "whoami-test/whoami@kubernetes" + "whoami-test-https-whoami-tls@kubernetes", + "whoami-test-https-whoami@kubernetes", + "whoami-test-whoami@kubernetes" ], "serverStatus": { "http://10.42.0.2:80": "UP", diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 51546914b..9e5f843a2 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -511,7 +511,7 @@ func makeID(namespace, name string) string { return name } - return namespace + "/" + name + return namespace + "-" + name } func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index dbdb7ef85..28c04d5d8 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -47,14 +47,14 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -79,19 +79,19 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", }, - "default/test.route-f44ce589164e656d231c": { + "default-test.route-f44ce589164e656d231c": { EntryPoints: []string{"foo"}, - Service: "default/test.route-f44ce589164e656d231c", + Service: "default-test.route-f44ce589164e656d231c", Rule: "HostSNI(`bar.com`)", }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -105,7 +105,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, }, - "default/test.route-f44ce589164e656d231c": { + "default-test.route-f44ce589164e656d231c": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -135,28 +135,28 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { - Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000", + Name: "default-test.route-fdd3e9338e47a45efefc-whoamitcp-8000", Weight: func(i int) *int { return &i }(2), }, { - Name: "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080", + Name: "default-test.route-fdd3e9338e47a45efefc-whoamitcp2-8080", Weight: func(i int) *int { return &i }(3), }, }, }, }, - "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000": { + "default-test.route-fdd3e9338e47a45efefc-whoamitcp-8000": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -168,7 +168,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, }, - "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080": { + "default-test.route-fdd3e9338e47a45efefc-whoamitcp2-8080": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -255,15 +255,15 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{}, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -292,9 +292,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ Passthrough: true, @@ -302,7 +302,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -332,7 +332,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -351,17 +351,17 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ - Options: "default/foo", + Options: "default-foo", }, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -390,7 +390,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "myns/foo": { + "myns-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -409,17 +409,17 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ - Options: "myns/foo", + Options: "myns-foo", }, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -448,7 +448,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -466,17 +466,17 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ - Options: "default/foo", + Options: "default-foo", }, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -505,24 +505,24 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", }, }, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ - Options: "default/unknown", + Options: "default-unknown", }, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -551,24 +551,24 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", }, }, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{ - Options: "unknown/foo", + Options: "unknown-foo", }, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -597,15 +597,15 @@ func TestLoadIngressRouteTCPs(t *testing.T) { expected: &dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", TLS: &dynamic.RouterTCPTLSConfig{}, }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -636,14 +636,14 @@ func TestLoadIngressRouteTCPs(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { EntryPoints: []string{"foo"}, - Service: "default/test.route-fdd3e9338e47a45efefc", + Service: "default-test.route-fdd3e9338e47a45efefc", Rule: "HostSNI(`foo.com`)", }, }, Services: map[string]*dynamic.TCPService{ - "default/test.route-fdd3e9338e47a45efefc": { + "default-test.route-fdd3e9338e47a45efefc": { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { @@ -718,16 +718,16 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -755,28 +755,28 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test2.route-23c7f4c450289ee29016": { + "default-test2.route-23c7f4c450289ee29016": { EntryPoints: []string{"web"}, - Service: "default/test2.route-23c7f4c450289ee29016", + Service: "default-test2.route-23c7f4c450289ee29016", Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)", Priority: 12, - Middlewares: []string{"default/stripprefix", "foo/addprefix"}, + Middlewares: []string{"default-stripprefix", "foo-addprefix"}, }, }, Middlewares: map[string]*dynamic.Middleware{ - "default/stripprefix": { + "default-stripprefix": { StripPrefix: &dynamic.StripPrefix{ Prefixes: []string{"/tobestripped"}, }, }, - "foo/addprefix": { + "foo-addprefix": { AddPrefix: &dynamic.AddPrefix{ Prefix: "/tobeadded", }, }, }, Services: map[string]*dynamic.Service{ - "default/test2.route-23c7f4c450289ee29016": { + "default-test2.route-23c7f4c450289ee29016": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -805,28 +805,28 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test2.route-23c7f4c450289ee29016": { + "default-test2.route-23c7f4c450289ee29016": { EntryPoints: []string{"web"}, - Service: "default/test2.route-23c7f4c450289ee29016", + Service: "default-test2.route-23c7f4c450289ee29016", Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)", Priority: 12, - Middlewares: []string{"default/stripprefix", "foo/addprefix", "basicauth@file", "redirect@file"}, + Middlewares: []string{"default-stripprefix", "foo-addprefix", "basicauth@file", "redirect@file"}, }, }, Middlewares: map[string]*dynamic.Middleware{ - "default/stripprefix": { + "default-stripprefix": { StripPrefix: &dynamic.StripPrefix{ Prefixes: []string{"/tobestripped"}, }, }, - "foo/addprefix": { + "foo-addprefix": { AddPrefix: &dynamic.AddPrefix{ Prefix: "/tobeadded", }, }, }, Services: map[string]*dynamic.Service{ - "default/test2.route-23c7f4c450289ee29016": { + "default-test2.route-23c7f4c450289ee29016": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -853,22 +853,22 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Priority: 14, }, - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, - Service: "default/test.route-77c62dfe9517144aeeaa", + Service: "default-test.route-77c62dfe9517144aeeaa", Rule: "Host(`foo.com`) && PathPrefix(`/foo`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -881,7 +881,7 @@ func TestLoadIngressRoutes(t *testing.T) { PassHostHeader: true, }, }, - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -910,30 +910,30 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, - Service: "default/test.route-77c62dfe9517144aeeaa", + Service: "default-test.route-77c62dfe9517144aeeaa", Rule: "Host(`foo.com`) && PathPrefix(`/foo`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { - Name: "default/test.route-77c62dfe9517144aeeaa-whoami-80", + Name: "default-test.route-77c62dfe9517144aeeaa-whoami-80", Weight: func(i int) *int { return &i }(1), }, { - Name: "default/test.route-77c62dfe9517144aeeaa-whoami2-8080", + Name: "default-test.route-77c62dfe9517144aeeaa-whoami2-8080", Weight: func(i int) *int { return &i }(1), }, }, }, }, - "default/test.route-77c62dfe9517144aeeaa-whoami-80": { + "default-test.route-77c62dfe9517144aeeaa-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -946,7 +946,7 @@ func TestLoadIngressRoutes(t *testing.T) { PassHostHeader: true, }, }, - "default/test.route-77c62dfe9517144aeeaa-whoami2-8080": { + "default-test.route-77c62dfe9517144aeeaa-whoami2-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -974,30 +974,30 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, - Service: "default/test.route-77c62dfe9517144aeeaa", + Service: "default-test.route-77c62dfe9517144aeeaa", Rule: "Host(`foo.com`) && PathPrefix(`/foo`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-77c62dfe9517144aeeaa": { + "default-test.route-77c62dfe9517144aeeaa": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { - Name: "default/test.route-77c62dfe9517144aeeaa-whoami-80", + Name: "default-test.route-77c62dfe9517144aeeaa-whoami-80", Weight: Int(10), }, { - Name: "default/test.route-77c62dfe9517144aeeaa-whoami2-8080", + Name: "default-test.route-77c62dfe9517144aeeaa-whoami2-8080", Weight: Int(0), }, }, }, }, - "default/test.route-77c62dfe9517144aeeaa-whoami-80": { + "default-test.route-77c62dfe9517144aeeaa-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1010,7 +1010,7 @@ func TestLoadIngressRoutes(t *testing.T) { PassHostHeader: true, }, }, - "default/test.route-77c62dfe9517144aeeaa-whoami2-8080": { + "default-test.route-77c62dfe9517144aeeaa-whoami2-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1112,9 +1112,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{}, @@ -1122,7 +1122,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1145,7 +1145,7 @@ func TestLoadIngressRoutes(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -1168,19 +1168,19 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{ - Options: "default/foo", + Options: "default-foo", }, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1203,7 +1203,7 @@ func TestLoadIngressRoutes(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "myns/foo": { + "myns-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -1226,19 +1226,19 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{ - Options: "myns/foo", + Options: "myns-foo", }, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1261,7 +1261,7 @@ func TestLoadIngressRoutes(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", CipherSuites: []string{ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -1283,19 +1283,19 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{ - Options: "default/foo", + Options: "default-foo", }, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1318,7 +1318,7 @@ func TestLoadIngressRoutes(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", }, }, @@ -1329,19 +1329,19 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{ - Options: "default/unknown", + Options: "default-unknown", }, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1364,7 +1364,7 @@ func TestLoadIngressRoutes(t *testing.T) { expected: &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Options: map[string]tls.Options{ - "default/foo": { + "default-foo": { MinVersion: "VersionTLS12", }, }, @@ -1375,19 +1375,19 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{ - Options: "unknown/foo", + Options: "unknown-foo", }, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1415,9 +1415,9 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, TLS: &dynamic.RouterTLSConfig{}, @@ -1425,7 +1425,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1453,16 +1453,16 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1490,16 +1490,16 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1528,17 +1528,17 @@ func TestLoadIngressRoutes(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{ - "default/basicauth": { + "default-basicauth": { BasicAuth: &dynamic.BasicAuth{ Users: dynamic.Users{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, }, }, - "default/digestauth": { + "default-digestauth": { DigestAuth: &dynamic.DigestAuth{ Users: dynamic.Users{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, }, }, - "default/forwardauth": { + "default-forwardauth": { ForwardAuth: &dynamic.ForwardAuth{ Address: "test.com", TLS: &dynamic.ClientTLS{ @@ -1565,16 +1565,16 @@ func TestLoadIngressRoutes(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{ - "default/errorpage": { + "default-errorpage": { Errors: &dynamic.ErrorPage{ Status: []string{"404", "500"}, - Service: "default/errorpage-errorpage-service", + Service: "default-errorpage-errorpage-service", Query: "query", }, }, }, Services: map[string]*dynamic.Service{ - "default/errorpage-errorpage-service": { + "default-errorpage-errorpage-service": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -1601,16 +1601,16 @@ func TestLoadIngressRoutes(t *testing.T) { }, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, - Service: "default/test.route-6b204d94623b3df4370c", + Service: "default-test.route-6b204d94623b3df4370c", Rule: "Host(`foo.com`) && PathPrefix(`/bar`)", Priority: 12, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default/test.route-6b204d94623b3df4370c": { + "default-test.route-6b204d94623b3df4370c": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 7a2d87a0b..51c6257bd 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -325,7 +325,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl continue } - serviceName := ingress.Namespace + "/" + p.Backend.ServiceName + "/" + p.Backend.ServicePort.String() + serviceName := ingress.Namespace + "-" + p.Backend.ServiceName + "-" + p.Backend.ServicePort.String() serviceName = strings.ReplaceAll(serviceName, ".", "-") var rules []string if len(rule.Host) > 0 { @@ -336,14 +336,18 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl rules = append(rules, "PathPrefix(`"+p.Path+"`)") } - conf.HTTP.Routers[strings.Replace(rule.Host, ".", "-", -1)+p.Path] = &dynamic.Router{ + routerKey := strings.Replace(rule.Host, ".", "-", -1) + strings.Replace(p.Path, "/", "-", 1) + if strings.HasPrefix(routerKey, "-") { + routerKey = strings.Replace(routerKey, "-", "", 1) + } + conf.HTTP.Routers[routerKey] = &dynamic.Router{ Rule: strings.Join(rules, " && "), Service: serviceName, } if len(ingress.Spec.TLS) > 0 { // TLS enabled for this ingress, add TLS router - conf.HTTP.Routers[strings.Replace(rule.Host, ".", "-", -1)+p.Path+"-tls"] = &dynamic.Router{ + conf.HTTP.Routers[routerKey+"-tls"] = &dynamic.Router{ Rule: strings.Join(rules, " && "), Service: serviceName, TLS: &dynamic.RouterTLSConfig{}, @@ -381,7 +385,7 @@ func getTLS(ctx context.Context, ingress *v1beta1.Ingress, k8sClient Client, tls continue } - configKey := ingress.Namespace + "/" + t.SecretName + configKey := ingress.Namespace + "-" + t.SecretName if _, tlsExists := tlsConfigs[configKey]; !tlsExists { secret, exists, err := k8sClient.GetSecret(ingress.Namespace, t.SecretName) if err != nil { diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index bc20a25b1..bcf2b0c13 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -43,13 +43,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -73,17 +73,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "/foo": { + "foo": { Rule: "PathPrefix(`/foo`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -107,17 +107,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "/foo": { + "foo": { Rule: "PathPrefix(`/foo`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -141,13 +141,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -172,11 +172,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Routers: map[string]*dynamic.Router{ "example-com": { Rule: "Host(`example.com`)", - Service: "testing/example-com/80", + Service: "testing-example-com-80", }, }, Services: map[string]*dynamic.Service{ - "testing/example-com/80": { + "testing-example-com-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -197,17 +197,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "traefik-tchouk/foo": { + "traefik-tchouk-foo": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/foo`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -231,17 +231,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "traefik-courgette/carotte": { + "traefik-courgette-carotte": { Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -265,17 +265,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "/foo": { + "foo": { Rule: "PathPrefix(`/foo`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -310,13 +310,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-courgette/carotte": { + "traefik-courgette-carotte": { Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -351,17 +351,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, - "traefik-courgette/carotte": { + "traefik-courgette-carotte": { Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", - Service: "testing/service2/8082", + Service: "testing-service2-8082", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -374,7 +374,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - "testing/service2/8082": { + "testing-service2-8082": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -451,13 +451,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -481,13 +481,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/tchouk", + Service: "testing-service1-tchouk", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/tchouk": { + "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -511,13 +511,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/tchouk", + Service: "testing-service1-tchouk", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/tchouk": { + "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -541,17 +541,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/tchouk", + Service: "testing-service1-tchouk", }, - "traefik-tchouk/foo": { + "traefik-tchouk-foo": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/foo`)", - Service: "testing/service1/carotte", + Service: "testing-service1-carotte", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/tchouk": { + "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -564,7 +564,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - "testing/service1/carotte": { + "testing-service1-carotte": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -588,17 +588,17 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/tchouk", + Service: "testing-service1-tchouk", }, - "toto-traefik-tchouk/bar": { + "toto-traefik-tchouk-bar": { Rule: "Host(`toto.traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "toto/service1/tchouk", + Service: "toto-service1-tchouk", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/tchouk": { + "testing-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -611,7 +611,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - "toto/service1/tchouk": { + "toto-service1-tchouk": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -657,13 +657,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "traefik-tchouk/bar": { + "traefik-tchouk-bar": { Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing/service1/8080", + Service: "testing-service1-8080", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/8080": { + "testing-service1-8080": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -686,16 +686,16 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Routers: map[string]*dynamic.Router{ "example-com": { Rule: "Host(`example.com`)", - Service: "testing/example-com/80", + Service: "testing-example-com-80", }, "example-com-tls": { Rule: "Host(`example.com`)", - Service: "testing/example-com/80", + Service: "testing-example-com-80", TLS: &dynamic.RouterTLSConfig{}, }, }, Services: map[string]*dynamic.Service{ - "testing/example-com/80": { + "testing-example-com-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -726,13 +726,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/443", + Service: "testing-service1-443", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/443": { + "testing-service1-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -756,13 +756,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/8443", + Service: "testing-service1-8443", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/8443": { + "testing-service1-8443": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -787,13 +787,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/8443", + Service: "testing-service1-8443", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/8443": { + "testing-service1-8443": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -848,13 +848,13 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ - "/bar": { + "bar": { Rule: "PathPrefix(`/bar`)", - Service: "testing/service1/80", + Service: "testing-service1-80", }, }, Services: map[string]*dynamic.Service{ - "testing/service1/80": { + "testing-service1-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ PassHostHeader: true, Servers: []dynamic.Server{ @@ -1088,13 +1088,13 @@ func TestGetTLS(t *testing.T) { }, }, result: map[string]*tls.CertAndStores{ - "testing/test-secret": { + "testing-test-secret": { Certificate: tls.Certificate{ CertFile: tls.FileOrContent("tls-crt"), KeyFile: tls.FileOrContent("tls-key"), }, }, - "testing/test-secret2": { + "testing-test-secret2": { Certificate: tls.Certificate{ CertFile: tls.FileOrContent("tls-crt"), KeyFile: tls.FileOrContent("tls-key"), From 7847b7685deacfe98dc287650b171bc5c50ae17b Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 13 Sep 2019 20:52:04 +0200 Subject: [PATCH 11/19] Prepare release v2.0.0-rc4 --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e896fac..e76e8497d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,21 @@ -# Change Log +## [v2.0.0-rc4](https://github.com/containous/traefik/tree/v2.0.0-rc4) (2019-09-13) +[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc3...v2.0.0-rc4) + +**Enhancements:** +- **[docker,k8s,k8s/crd,marathon,rancher,tcp]** Add weighted round robin load balancer on TCP ([#5380](https://github.com/containous/traefik/pull/5380) by [juliens](https://github.com/juliens)) +- **[k8s,k8s/crd,k8s/ingress]** Fix kubernetes id name ([#5383](https://github.com/containous/traefik/pull/5383) by [mmatur](https://github.com/mmatur)) +- **[k8s,k8s/crd]** Add passHostHeader and responseForwarding in IngressRoute ([#5368](https://github.com/containous/traefik/pull/5368) by [juliens](https://github.com/juliens)) + +**Bug fixes:** +- **[api,webui]** Improve documentation about API and Dashboard. ([#5364](https://github.com/containous/traefik/pull/5364) by [ldez](https://github.com/ldez)) +- **[cli]** fix: boolean flag parsing with map. ([#5372](https://github.com/containous/traefik/pull/5372) by [ldez](https://github.com/ldez)) +- **[logs]** fix: logger and context. ([#5370](https://github.com/containous/traefik/pull/5370) by [ldez](https://github.com/ldez)) +- **[tcp]** On client CloseWrite, do CloseWrite instead of Close for backend ([#5366](https://github.com/containous/traefik/pull/5366) by [juliens](https://github.com/juliens)) +- **[webui]** fix: passHostHeader in the webUI. ([#5369](https://github.com/containous/traefik/pull/5369) by [ldez](https://github.com/ldez)) + +**Documentation:** +- fix a service with one server .yaml example ([#5373](https://github.com/containous/traefik/pull/5373) by [zaverden](https://github.com/zaverden)) +- fix: services configuration documentation. ([#5359](https://github.com/containous/traefik/pull/5359) by [ldez](https://github.com/ldez)) ## [v2.0.0-rc3](https://github.com/containous/traefik/tree/v2.0.0-rc3) (2019-09-10) [All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc2...v2.0.0-rc3) From 93626de01cb16801431d10cc366e93390e9556e4 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 16 Sep 2019 10:44:03 +0200 Subject: [PATCH 12/19] fix: invalid hash for github.com/labbsr0x/goh --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c910ddf8f..512bcc464 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/huandu/xstrings v1.2.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc github.com/instana/go-sensor v1.4.17-0.20190515112224-78c14625025a - github.com/labbsr0x/goh v0.0.0-20190610190554-60aa50bcbca7 // indirect + github.com/labbsr0x/goh v0.0.0-20190830205702-3d6988c73e10 // indirect github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad github.com/libkermit/docker v0.0.0-20171122101128-e6674d32b807 github.com/libkermit/docker-check v0.0.0-20171122104347-1113af38e591 diff --git a/go.sum b/go.sum index aa48a1794..d062717cd 100644 --- a/go.sum +++ b/go.sum @@ -307,8 +307,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.0 h1:gooRvyQtVOCtV/l9ZCI4CManZeVN/kUWG/vugRqHqv4= github.com/labbsr0x/bindman-dns-webhook v1.0.0/go.mod h1:pn4jcNjxSywRWDPDyGkFzgSnwty18OFdiUFc6S6fpgc= github.com/labbsr0x/goh v0.0.0-20190417202808-8b16b4848295/go.mod h1:RBxeaayaaMmp7GxwHiKANjkg9e+rxjOm4mB5vD5rt/I= -github.com/labbsr0x/goh v0.0.0-20190610190554-60aa50bcbca7 h1:ocfbpesrzMqybD816LDEjourU4jiHj6gDGMieAcU8Io= -github.com/labbsr0x/goh v0.0.0-20190610190554-60aa50bcbca7/go.mod h1:RBxeaayaaMmp7GxwHiKANjkg9e+rxjOm4mB5vD5rt/I= +github.com/labbsr0x/goh v0.0.0-20190830205702-3d6988c73e10 h1:mrPTy7qNJPGHaUkkN301r8Y+13l2/vsiC8Lvi09e6sI= +github.com/labbsr0x/goh v0.0.0-20190830205702-3d6988c73e10/go.mod h1:RBxeaayaaMmp7GxwHiKANjkg9e+rxjOm4mB5vD5rt/I= github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad h1:nTyRWZ864mnHUnusBCVA628AZFgfGHwRUpbHqGhRQr8= github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad/go.mod h1:GyCk/ifDcqsU1tsRMMWqXANnTtxzcwEWscb7j5qmblM= github.com/libkermit/docker v0.0.0-20171122101128-e6674d32b807 h1:/7J1WDQd6Xn1Pr8KtE2I/7/cKw66AV3hBUOyxqyXo84= From 4b966f1f82003987d294837b99faf42c48bfad5f Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 16 Sep 2019 15:18:06 +0200 Subject: [PATCH 13/19] Web UI graph names. --- webui/quasar.conf.js | 1 + webui/src/components/dashboard/PanelEntry.vue | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/webui/quasar.conf.js b/webui/quasar.conf.js index 109fa6130..b25ee4f02 100644 --- a/webui/quasar.conf.js +++ b/webui/quasar.conf.js @@ -47,6 +47,7 @@ module.exports = function (ctx) { 'QToolbar', 'QSpace', 'QToolbarTitle', + 'QTooltip', 'QBtn', 'QIcon', 'QList', diff --git a/webui/src/components/dashboard/PanelEntry.vue b/webui/src/components/dashboard/PanelEntry.vue index 2ae06af45..1aac12e98 100644 --- a/webui/src/components/dashboard/PanelEntry.vue +++ b/webui/src/components/dashboard/PanelEntry.vue @@ -8,7 +8,10 @@
-
{{address}}
+
+ {{address}} + {{address}} +
From 673351d82166fb2ac72b464401cbcf50eef7a8c3 Mon Sep 17 00:00:00 2001 From: Adrien Brignon Date: Mon, 16 Sep 2019 15:34:05 +0200 Subject: [PATCH 14/19] Fix typo in documentation --- docs/content/observability/metrics/influxdb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/observability/metrics/influxdb.md b/docs/content/observability/metrics/influxdb.md index c7e0a93c0..71c8ebd70 100644 --- a/docs/content/observability/metrics/influxdb.md +++ b/docs/content/observability/metrics/influxdb.md @@ -47,7 +47,7 @@ InfluxDB's address protocol (udp or http). ```toml tab="File (TOML)" [metrics] [metrics.influxdb] - protocol = "upd" + protocol = "udp" ``` ```yaml tab="File (YAML)" From e70c8a7b461a57a2c7bbafb1895258b54d7e5135 Mon Sep 17 00:00:00 2001 From: MycTl <1701653+MycTl@users.noreply.github.com> Date: Mon, 16 Sep 2019 16:36:03 +0200 Subject: [PATCH 15/19] Fix error in the documentation for CLI configuration example --- docs/content/https/acme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 7a03295ae..02dec23cb 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -50,10 +50,10 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom --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 + --certificatesResolvers.sample.acme.httpChallenge.entryPoint=web ``` ??? note "Configuration Reference" From 47a9b086eaf4b6a60a7a80631000cb1559e915e3 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 16 Sep 2019 17:26:06 +0200 Subject: [PATCH 16/19] Disable collect data by default. --- cmd/traefik/traefik.go | 22 +++++++------------- docs/content/contributing/data-collection.md | 20 ------------------ pkg/anonymize/anonymize_config_test.go | 3 +-- pkg/config/static/static_config.go | 4 ++-- 4 files changed, 10 insertions(+), 39 deletions(-) diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 8702855b9..d0d55e55d 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -286,24 +286,16 @@ func checkNewVersion() { } func stats(staticConfiguration *static.Configuration) { - if staticConfiguration.Global.SendAnonymousUsage == nil { - log.WithoutContext().Error(` -You haven't specified the sendAnonymousUsage option, it will be enabled by default. -`) - sendAnonymousUsage := true - staticConfiguration.Global.SendAnonymousUsage = &sendAnonymousUsage - } + logger := log.WithoutContext() - if *staticConfiguration.Global.SendAnonymousUsage { - log.WithoutContext().Info(` -Stats collection is enabled. -Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration. -Help us improve Traefik by leaving this feature on :) -More details on: https://docs.traefik.io/v2.0/contributing/data-collection/ -`) + if staticConfiguration.Global.SendAnonymousUsage { + logger.Info(`Stats collection is enabled.`) + logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`) + logger.Info(`Help us improve Traefik by leaving this feature on :)`) + logger.Info(`More details on: https://docs.traefik.io/v2.0/contributing/data-collection/`) collect(staticConfiguration) } else { - log.WithoutContext().Info(` + logger.Info(` Stats collection is disabled. Help us improve Traefik by turning this feature on :) More details on: https://docs.traefik.io/v2.0/contributing/data-collection/ diff --git a/docs/content/contributing/data-collection.md b/docs/content/contributing/data-collection.md index 4442fc78e..b8950c7cb 100644 --- a/docs/content/contributing/data-collection.md +++ b/docs/content/contributing/data-collection.md @@ -8,9 +8,6 @@ Understanding How Traefik is Being Used Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways. For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us so we can benefit from your experience and use cases. -!!! warning - Before the GA, leaving this option unset will not prevent Traefik from running but will generate an error log indicating that it enables data collection by default. - !!! example "Enabling Data Collection" ```toml tab="File (TOML)" @@ -71,14 +68,6 @@ Once a day (the first call begins 10 minutes after the start of Traefik), we col cert = "dockerCert" key = "dockerKey" insecureSkipVerify = true - - [providers.ecs] - domain = "foo.bar" - exposedByDefault = true - clusters = ["foo-bar"] - region = "us-west-2" - accessKeyID = "AccessKeyID" - secretAccessKey = "SecretAccessKey" ``` ??? example "Resulting Obfuscated Configuration" @@ -92,7 +81,6 @@ Once a day (the first call begins 10 minutes after the start of Traefik), we col [providers.docker] endpoint = "xxxx" - domain = "xxxx" exposedByDefault = true swarmMode = true @@ -101,14 +89,6 @@ Once a day (the first call begins 10 minutes after the start of Traefik), we col cert = "xxxx" key = "xxxx" insecureSkipVerify = false - - [providers.ecs] - domain = "xxxx" - exposedByDefault = true - clusters = [] - region = "us-west-2" - accessKeyID = "xxxx" - secretAccessKey = "xxxx" ``` ## The Code for Data Collection diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/anonymize/anonymize_config_test.go index 683c70a1b..ccbc744ce 100644 --- a/pkg/anonymize/anonymize_config_test.go +++ b/pkg/anonymize/anonymize_config_test.go @@ -26,10 +26,9 @@ import ( func TestDo_globalConfiguration(t *testing.T) { config := &static.Configuration{} - sendAnonymousUsage := true config.Global = &static.Global{ CheckNewVersion: true, - SendAnonymousUsage: &sendAnonymousUsage, + SendAnonymousUsage: true, } config.AccessLog = &types.AccessLog{ diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 38b3569bb..6921425c7 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -71,8 +71,8 @@ type CertificateResolver struct { // Global holds the global configuration. type Global struct { - CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" export:"true"` - SendAnonymousUsage *bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" export:"true"` + CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" export:"true"` + SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" export:"true"` } // ServersTransport options to configure communication between Traefik and the servers From 3d784a14f9a5b6f7631af87498a7c1f35664a5b1 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 16 Sep 2019 18:28:04 +0200 Subject: [PATCH 17/19] Prepare release v2.0.0 --- CHANGELOG.md | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e76e8497d..5ba317c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,328 @@ +## [v2.0.0](https://github.com/containous/traefik/tree/v2.0.0) (2019-09-16) +[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha1...v2.0.0) + +**Enhancements:** +- **[acme,api,tracing]** New API security ([#5311](https://github.com/containous/traefik/pull/5311) by [juliens](https://github.com/juliens)) +- **[acme,k8s,k8s/crd]** Document the TLS with ACME case ([#4654](https://github.com/containous/traefik/pull/4654) by [mpl](https://github.com/mpl)) +- **[acme,kv]** Remove Deprecated StorageFile ([#4252](https://github.com/containous/traefik/pull/4252) by [juliens](https://github.com/juliens)) +- **[acme]** Remove timeout/interval from the ACME Provider ([#4842](https://github.com/containous/traefik/pull/4842) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[acme]** Certificate resolvers. ([#5116](https://github.com/containous/traefik/pull/5116) by [ldez](https://github.com/ldez)) +- **[acme]** Improve acme logs. ([#5139](https://github.com/containous/traefik/pull/5139) by [ldez](https://github.com/ldez)) +- **[acme]** Migrate to go-acme/lego. ([#4589](https://github.com/containous/traefik/pull/4589) by [ldez](https://github.com/ldez)) +- **[api,provider]** Enhance REST provider ([#5072](https://github.com/containous/traefik/pull/5072) by [dtomcej](https://github.com/dtomcej)) +- **[api]** Adding content-header to api endpoints ([#5019](https://github.com/containous/traefik/pull/5019) by [dalanmiller](https://github.com/dalanmiller)) +- **[api]** Deal with multiple errors and their criticality ([#5070](https://github.com/containous/traefik/pull/5070) by [mpl](https://github.com/mpl)) +- **[api]** API: remove configuration of Entrypoint and Middlewares ([#5119](https://github.com/containous/traefik/pull/5119) by [mpl](https://github.com/mpl)) +- **[api]** Improve API endpoints ([#5080](https://github.com/containous/traefik/pull/5080) by [ldez](https://github.com/ldez)) +- **[api]** API: new contract ([#4964](https://github.com/containous/traefik/pull/4964) by [mpl](https://github.com/mpl)) +- **[api]** Improve API for the web UI ([#5267](https://github.com/containous/traefik/pull/5267) by [ldez](https://github.com/ldez)) +- **[api]** Manage status for TCP element in the endpoint overview. ([#5108](https://github.com/containous/traefik/pull/5108) by [ldez](https://github.com/ldez)) +- **[api]** API: expose runtime representation ([#4841](https://github.com/containous/traefik/pull/4841) by [mpl](https://github.com/mpl)) +- **[authentication,middleware,k8s,k8s/crd]** Auth middlewares in kubernetes CRD use secrets ([#5299](https://github.com/containous/traefik/pull/5299) by [juliens](https://github.com/juliens)) +- **[authentication,logs,etcd]** Remove deprecated elements ([#3715](https://github.com/containous/traefik/pull/3715) by [geraldcroes](https://github.com/geraldcroes)) +- **[authentication,middleware]** Basic Auth custom realm ([#3917](https://github.com/containous/traefik/pull/3917) by [tcoupin](https://github.com/tcoupin)) +- **[cli]** New static configuration loading system. ([#4935](https://github.com/containous/traefik/pull/4935) by [ldez](https://github.com/ldez)) +- **[docker,k8s,k8s/crd,k8s/ingress]** chore: update docker and k8s ([#5174](https://github.com/containous/traefik/pull/5174) by [ldez](https://github.com/ldez)) +- **[docker,k8s,k8s/crd,marathon,rancher,tcp]** Add weighted round robin load balancer on TCP ([#5380](https://github.com/containous/traefik/pull/5380) by [juliens](https://github.com/juliens)) +- **[docker,tcp]** Add support for TCP labels in Docker provider ([#4621](https://github.com/containous/traefik/pull/4621) by [juliens](https://github.com/juliens)) +- **[docker]** Adds default rule system on Docker provider. ([#4413](https://github.com/containous/traefik/pull/4413) by [ldez](https://github.com/ldez)) +- **[docker]** Adds Docker provider support ([#4399](https://github.com/containous/traefik/pull/4399) by [ldez](https://github.com/ldez)) +- **[docker]** Update to Go1.12. Support of TLS1.3 ([#4540](https://github.com/containous/traefik/pull/4540) by [ldez](https://github.com/ldez)) +- **[etcd]** Remove etcd v2 ([#3739](https://github.com/containous/traefik/pull/3739) by [geraldcroes](https://github.com/geraldcroes)) +- **[file]** Restrict traefik.toml to static configuration. ([#5090](https://github.com/containous/traefik/pull/5090) by [ldez](https://github.com/ldez)) +- **[file]** Support YAML for the dynamic configuration. ([#5024](https://github.com/containous/traefik/pull/5024) by [ldez](https://github.com/ldez)) +- **[k8s,k8s/crd,k8s/ingress]** Correct Kubernetes Ingress and IngressRoute port heuristic for choosing HTTPS ([#5167](https://github.com/containous/traefik/pull/5167) by [seh](https://github.com/seh)) +- **[k8s,k8s/crd,k8s/ingress]** Fix kubernetes id name ([#5383](https://github.com/containous/traefik/pull/5383) by [mmatur](https://github.com/mmatur)) +- **[k8s,k8s/crd,tcp]** Add support for TCP (in kubernetes CRD) ([#4885](https://github.com/containous/traefik/pull/4885) by [mpl](https://github.com/mpl)) +- **[k8s,k8s/crd,tls]** Define TLS options on the Router configuration for Kubernetes ([#4973](https://github.com/containous/traefik/pull/4973) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[k8s,k8s/crd]** Add passHostHeader and responseForwarding in IngressRoute ([#5368](https://github.com/containous/traefik/pull/5368) by [juliens](https://github.com/juliens)) +- **[k8s,k8s/crd]** Add scheme to IngressRoute. ([#5062](https://github.com/containous/traefik/pull/5062) by [ldez](https://github.com/ldez)) +- **[k8s,k8s/ingress]** Renamed `kubernetes` provider in `kubernetesIngress` provider ([#5068](https://github.com/containous/traefik/pull/5068) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[k8s,k8s/ingress]** Add TLS-enabled Router ([#5162](https://github.com/containous/traefik/pull/5162) by [dtomcej](https://github.com/dtomcej)) +- **[k8s/ingress]** Adds Kubernetes provider support ([#4476](https://github.com/containous/traefik/pull/4476) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[k8s/ingress]** Adds update ingress status ([#4603](https://github.com/containous/traefik/pull/4603) by [juliens](https://github.com/juliens)) +- **[k8s/ingress]** k8s integration tests ([#4569](https://github.com/containous/traefik/pull/4569) by [juliens](https://github.com/juliens)) +- **[k8s/ingress]** Custom resource definition ([#4591](https://github.com/containous/traefik/pull/4591) by [ldez](https://github.com/ldez)) +- **[logs]** Improve error on router without service. ([#5126](https://github.com/containous/traefik/pull/5126) by [ldez](https://github.com/ldez)) +- **[logs]** log.loglevel becomes log.level in configuration ([#4775](https://github.com/containous/traefik/pull/4775) by [juliens](https://github.com/juliens)) +- **[logs]** Drop headers by default in access logs. ([#5034](https://github.com/containous/traefik/pull/5034) by [ldez](https://github.com/ldez)) +- **[logs]** Default to CLF when accesslog format is unsupported ([#5314](https://github.com/containous/traefik/pull/5314) by [mpl](https://github.com/mpl)) +- **[marathon,tcp]** Handle TCP in the marathon provider ([#4728](https://github.com/containous/traefik/pull/4728) by [juliens](https://github.com/juliens)) +- **[marathon]** Adds Marathon support. ([#4415](https://github.com/containous/traefik/pull/4415) by [ldez](https://github.com/ldez)) +- **[metrics]** Add Metrics ([#5111](https://github.com/containous/traefik/pull/5111) by [mmatur](https://github.com/mmatur)) +- **[metrics]** Add HTTP authentication to influxdb metric backend ([#3600](https://github.com/containous/traefik/pull/3600) by [halfa](https://github.com/halfa)) +- **[middleware,k8s,k8s/crd]** k8s ErrorPage middleware now uses k8s service ([#5339](https://github.com/containous/traefik/pull/5339) by [juliens](https://github.com/juliens)) +- **[middleware,k8s/crd]** Handle cross-provider middleware in kubernetes CRD ([#5009](https://github.com/containous/traefik/pull/5009) by [mpl](https://github.com/mpl)) +- **[middleware,provider]** Change the provider separator from . to @ ([#4982](https://github.com/containous/traefik/pull/4982) by [ldez](https://github.com/ldez)) +- **[middleware,provider]** Add Feature-Policy header support ([#5156](https://github.com/containous/traefik/pull/5156) by [dtomcej](https://github.com/dtomcej)) +- **[middleware,tracing]** Re enable ratelimit integration tests ([#5288](https://github.com/containous/traefik/pull/5288) by [mmatur](https://github.com/mmatur)) +- **[middleware,provider]** IPStrategy for selecting IP in whitelist ([#3778](https://github.com/containous/traefik/pull/3778) by [juliens](https://github.com/juliens)) +- **[middleware,provider]** Enables the use of elements declared in other providers ([#4372](https://github.com/containous/traefik/pull/4372) by [geraldcroes](https://github.com/geraldcroes)) +- **[middleware]** Migrates the pass client tls cert middleware ([#4373](https://github.com/containous/traefik/pull/4373) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[middleware]** Migrates Compress from bool to struct ([#3714](https://github.com/containous/traefik/pull/3714) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[middleware]** Updates for jaeger tracing client. ([#3688](https://github.com/containous/traefik/pull/3688) by [tcolgate](https://github.com/tcolgate)) +- **[middleware]** Add forwarded headers on entry point configuration ([#4364](https://github.com/containous/traefik/pull/4364) by [juliens](https://github.com/juliens)) +- **[middleware]** SchemeRedirect Middleware ([#4400](https://github.com/containous/traefik/pull/4400) by [geraldcroes](https://github.com/geraldcroes)) +- **[middleware]** Add rate limiter, rename maxConn into inFlightReq ([#5246](https://github.com/containous/traefik/pull/5246) by [mpl](https://github.com/mpl)) +- **[middleware]** Disable RateLimit temporarily ([#5123](https://github.com/containous/traefik/pull/5123) by [juliens](https://github.com/juliens)) +- **[middleware]** Enable CORS configuration ([#3809](https://github.com/containous/traefik/pull/3809) by [dtomcej](https://github.com/dtomcej)) +- **[provider]** New constraints management. ([#4965](https://github.com/containous/traefik/pull/4965) by [ldez](https://github.com/ldez)) +- **[provider]** Remove BaseProvider ([#4661](https://github.com/containous/traefik/pull/4661) by [ldez](https://github.com/ldez)) +- **[provider]** Use name@provider instead of provider@name. ([#4990](https://github.com/containous/traefik/pull/4990) by [ldez](https://github.com/ldez)) +- **[provider]** Add health check timeout parameter ([#3813](https://github.com/containous/traefik/pull/3813) by [jbiel](https://github.com/jbiel)) +- **[provider]** Removes deprecated templates ([#3649](https://github.com/containous/traefik/pull/3649) by [geraldcroes](https://github.com/geraldcroes)) +- **[provider]** Remove everything templates related ([#4595](https://github.com/containous/traefik/pull/4595) by [mpl](https://github.com/mpl)) +- **[provider]** Small code enhancements on providers ([#3707](https://github.com/containous/traefik/pull/3707) by [vdemeester](https://github.com/vdemeester)) +- **[provider]** Migrate rest provider ([#4253](https://github.com/containous/traefik/pull/4253) by [juliens](https://github.com/juliens)) +- **[provider]** Labels parser. ([#4236](https://github.com/containous/traefik/pull/4236) by [ldez](https://github.com/ldez)) +- **[rancher]** Add Rancher provider ([#4647](https://github.com/containous/traefik/pull/4647) by [SantoDE](https://github.com/SantoDE)) +- **[rules]** New rule syntax ([#4437](https://github.com/containous/traefik/pull/4437) by [juliens](https://github.com/juliens)) +- **[server]** Adds mirroring service ([#5251](https://github.com/containous/traefik/pull/5251) by [juliens](https://github.com/juliens)) +- **[server]** Add support proxyprotocol v2 ([#4755](https://github.com/containous/traefik/pull/4755) by [c0va23](https://github.com/c0va23)) +- **[server]** WeightedRoundRobin load balancer ([#5237](https://github.com/containous/traefik/pull/5237) by [juliens](https://github.com/juliens)) +- **[server]** Make HTTP Keep-Alive timeout configurable for backend connections ([#4983](https://github.com/containous/traefik/pull/4983) by [mszabo-wikia](https://github.com/mszabo-wikia)) +- **[server]** Rework loadbalancer support ([#4933](https://github.com/containous/traefik/pull/4933) by [juliens](https://github.com/juliens)) +- **[server]** Use h2c from x/net to handle h2c requests ([#5045](https://github.com/containous/traefik/pull/5045) by [juliens](https://github.com/juliens)) +- **[server]** Dynamic Configuration Refactoring ([#4168](https://github.com/containous/traefik/pull/4168) by [ldez](https://github.com/ldez)) +- **[server]** Remove old global config and use new static config ([#4222](https://github.com/containous/traefik/pull/4222) by [juliens](https://github.com/juliens)) +- **[sticky-session]** HttpOnly and Secure flags on the affinity cookie ([#4947](https://github.com/containous/traefik/pull/4947) by [gheibia](https://github.com/gheibia)) +- **[tcp]** Adds TCP support ([#4587](https://github.com/containous/traefik/pull/4587) by [juliens](https://github.com/juliens)) +- **[tls]** Define a TLS section to group TLS, TLSOptions, and TLSStores. ([#5031](https://github.com/containous/traefik/pull/5031) by [ldez](https://github.com/ldez)) +- **[tls]** TLSOptions: handle conflict: same host name, different TLS options ([#5056](https://github.com/containous/traefik/pull/5056) by [mpl](https://github.com/mpl)) +- **[tls]** Define TLS options on the Router configuration ([#4931](https://github.com/containous/traefik/pull/4931) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[tls]** Expand Client Auth Type configuration ([#5078](https://github.com/containous/traefik/pull/5078) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[tracing]** Improve tracing ([#5010](https://github.com/containous/traefik/pull/5010) by [mmatur](https://github.com/mmatur)) +- **[tracing]** Add Jaeger collector endpoint ([#5082](https://github.com/containous/traefik/pull/5082) by [rmfitzpatrick](https://github.com/rmfitzpatrick)) +- **[tracing]** Update tracing dependencies ([#4721](https://github.com/containous/traefik/pull/4721) by [ldez](https://github.com/ldez)) +- **[tracing]** Added support for Haystack tracing ([#4555](https://github.com/containous/traefik/pull/4555) by [aantono](https://github.com/aantono)) +- **[tracing]** Update Zipkin OpenTracing driver to latest 0.4.3 release ([#5283](https://github.com/containous/traefik/pull/5283) by [basvanbeek](https://github.com/basvanbeek)) +- **[tracing]** Instana tracer implementation ([#4453](https://github.com/containous/traefik/pull/4453) by [notsureifkevin](https://github.com/notsureifkevin)) +- **[tracing]** Make Zipkin trace rate configurable ([#3968](https://github.com/containous/traefik/pull/3968) by [negz](https://github.com/negz)) +- **[webui]** refactor(webui): use @vue/cli to bootstrap new ui ([#5091](https://github.com/containous/traefik/pull/5091) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Add a new dashboard page ([#5249](https://github.com/containous/traefik/pull/5249) by [Basgrani](https://github.com/Basgrani)) +- **[webui]** Add doc and version in navbar ([#5137](https://github.com/containous/traefik/pull/5137) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Use components to split Home concerns ([#5136](https://github.com/containous/traefik/pull/5136) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Add more pages in the WebUI ([#5278](https://github.com/containous/traefik/pull/5278) by [Basgrani](https://github.com/Basgrani)) +- **[webui]** feat(webui/dashboard): init new dashboard ([#5105](https://github.com/containous/traefik/pull/5105) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Upgrade angular cli version ([#4450](https://github.com/containous/traefik/pull/4450) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Update docker node version ([#4448](https://github.com/containous/traefik/pull/4448) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Ignore target/dependencies in docker copy ([#4449](https://github.com/containous/traefik/pull/4449) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Format code with prettier ([#4463](https://github.com/containous/traefik/pull/4463) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** No need for npm progress=false ([#3702](https://github.com/containous/traefik/pull/3702) by [vdemeester](https://github.com/vdemeester)) +- **[webui]** Migrate to a work in progress webui ([#4568](https://github.com/containous/traefik/pull/4568) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Include lint in build process ([#4462](https://github.com/containous/traefik/pull/4462) by [Slashgear](https://github.com/Slashgear)) +- **[webui]** Dropping rxjs-compat in favor of pipe ([#4520](https://github.com/containous/traefik/pull/4520) by [imcotton](https://github.com/imcotton)) +- Move dynamic config into a dedicated package. ([#5075](https://github.com/containous/traefik/pull/5075) by [ldez](https://github.com/ldez)) +- Disable collect data by default. ([#5393](https://github.com/containous/traefik/pull/5393) by [ldez](https://github.com/ldez)) +- Bump x/sys to support Risc-V architecture ([#5245](https://github.com/containous/traefik/pull/5245) by [carlosedp](https://github.com/carlosedp)) +- New packaging system. ([#4593](https://github.com/containous/traefik/pull/4593) by [ldez](https://github.com/ldez)) +- Updates Backoff ([#4457](https://github.com/containous/traefik/pull/4457) by [ldez](https://github.com/ldez)) +- Remove the bug command ([#4556](https://github.com/containous/traefik/pull/4556) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Small code enhancements ([#3712](https://github.com/containous/traefik/pull/3712) by [mmatur](https://github.com/mmatur)) +- Remove deprecated elements ([#3666](https://github.com/containous/traefik/pull/3666) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Clean old ([#4612](https://github.com/containous/traefik/pull/4612) by [ldez](https://github.com/ldez)) +- Update anonymize/collect ([#4590](https://github.com/containous/traefik/pull/4590) by [jbdoumenjou](https://github.com/jbdoumenjou)) + +**Bug fixes:** +- **[api,webui]** Improve documentation about API and Dashboard. ([#5364](https://github.com/containous/traefik/pull/5364) by [ldez](https://github.com/ldez)) +- **[api]** Add errors about unknown entryPoint in runtime api ([#5265](https://github.com/containous/traefik/pull/5265) by [juliens](https://github.com/juliens)) +- **[api]** Add provider in middleware chain ([#5334](https://github.com/containous/traefik/pull/5334) by [juliens](https://github.com/juliens)) +- **[cli]** fix: boolean flag parsing with map. ([#5372](https://github.com/containous/traefik/pull/5372) by [ldez](https://github.com/ldez)) +- **[cli]** Return an error when help is called on a non existing command. ([#4977](https://github.com/containous/traefik/pull/4977) by [ldez](https://github.com/ldez)) +- **[cli]** Filter env vars configuration ([#4985](https://github.com/containous/traefik/pull/4985) by [ldez](https://github.com/ldez)) +- **[cli]** Fix some CLI bugs ([#4989](https://github.com/containous/traefik/pull/4989) by [ldez](https://github.com/ldez)) +- **[cli]** Change the loading resource order ([#5007](https://github.com/containous/traefik/pull/5007) by [ldez](https://github.com/ldez)) +- **[cli]** Apply the case of the CLI flags for the configuration ([#5153](https://github.com/containous/traefik/pull/5153) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[cli]** Don't allow non flag arguments by default. ([#4970](https://github.com/containous/traefik/pull/4970) by [ldez](https://github.com/ldez)) +- **[docker]** Insensitive case for allow-empty value. ([#4745](https://github.com/containous/traefik/pull/4745) by [ldez](https://github.com/ldez)) +- **[file]** fix: TLS configuration from directory. ([#5118](https://github.com/containous/traefik/pull/5118) by [ldez](https://github.com/ldez)) +- **[k8s,k8s/crd]** Fix log messages about label selector ([#4629](https://github.com/containous/traefik/pull/4629) by [mpl](https://github.com/mpl)) +- **[k8s,k8s/crd]** fix: TLS domains with IngressRoute. ([#5327](https://github.com/containous/traefik/pull/5327) by [ldez](https://github.com/ldez)) +- **[k8s,k8s/crd]** Remove IngressEndpoint in CRD provider ([#4616](https://github.com/containous/traefik/pull/4616) by [juliens](https://github.com/juliens)) +- **[logs]** fix: logger and context. ([#5370](https://github.com/containous/traefik/pull/5370) by [ldez](https://github.com/ldez)) +- **[logs]** fix: error log message. ([#5020](https://github.com/containous/traefik/pull/5020) by [ldez](https://github.com/ldez)) +- **[logs]** Fix typos in data collection message ([#4891](https://github.com/containous/traefik/pull/4891) by [mpl](https://github.com/mpl)) +- **[logs]** Allow user to configure traefik log ([#4604](https://github.com/containous/traefik/pull/4604) by [mmatur](https://github.com/mmatur)) +- **[metrics,tracing]** fix: Datadog case. ([#5272](https://github.com/containous/traefik/pull/5272) by [ldez](https://github.com/ldez)) +- **[metrics]** Fix prometheus metrics ([#5152](https://github.com/containous/traefik/pull/5152) by [mmatur](https://github.com/mmatur)) +- **[middleware,k8s,k8s/crd]** The chain middleware in k8s use middlewareRef ([#5290](https://github.com/containous/traefik/pull/5290) by [juliens](https://github.com/juliens)) +- **[middleware]** Set X-Forwarded-* headers ([#4707](https://github.com/containous/traefik/pull/4707) by [mpl](https://github.com/mpl)) +- **[middleware]** Fix `url.Parse` due to go1.12.8 changes. ([#5207](https://github.com/containous/traefik/pull/5207) by [ldez](https://github.com/ldez)) +- **[middleware]** fix: stripPrefix and stripPrefixRegex. ([#5291](https://github.com/containous/traefik/pull/5291) by [ldez](https://github.com/ldez)) +- **[middleware]** Improve rate limiter tests ([#5310](https://github.com/containous/traefik/pull/5310) by [mpl](https://github.com/mpl)) +- **[middleware]** Fix response modifier initial building ([#4719](https://github.com/containous/traefik/pull/4719) by [mpl](https://github.com/mpl)) +- **[middleware]** Remove X-Forwarded-(Uri, Method, Tls-Client-Cert and Tls-Client-Cert-Info) from untrusted IP ([#5012](https://github.com/containous/traefik/pull/5012) by [stffabi](https://github.com/stffabi)) +- **[middleware]** fix buffering middleware ([#5281](https://github.com/containous/traefik/pull/5281) by [ldez](https://github.com/ldez)) +- **[middleware]** Don't panic with undefined middleware ([#5289](https://github.com/containous/traefik/pull/5289) by [ldez](https://github.com/ldez)) +- **[middleware]** Properly add response headers for CORS ([#4857](https://github.com/containous/traefik/pull/4857) by [dtomcej](https://github.com/dtomcej)) +- **[rules]** Allow matching with FQDN hosts with trailing periods ([#4763](https://github.com/containous/traefik/pull/4763) by [dtomcej](https://github.com/dtomcej)) +- **[server]** Fix panic while server shutdown ([#4644](https://github.com/containous/traefik/pull/4644) by [juliens](https://github.com/juliens)) +- **[server]** Write HTTP server logs into the global logger. ([#5329](https://github.com/containous/traefik/pull/5329) by [ldez](https://github.com/ldez)) +- **[server]** Fix problem in aggregator provider ([#4625](https://github.com/containous/traefik/pull/4625) by [juliens](https://github.com/juliens)) +- **[server]** Fix lock problem in server ([#4600](https://github.com/containous/traefik/pull/4600) by [juliens](https://github.com/juliens)) +- **[service,websocket]** Fix recovered panic when websocket is mirrored ([#5255](https://github.com/containous/traefik/pull/5255) by [juliens](https://github.com/juliens)) +- **[tcp]** Fix EOF error ([#4733](https://github.com/containous/traefik/pull/4733) by [juliens](https://github.com/juliens)) +- **[tcp]** Don't add TCP proxy when error occurs during creation. ([#4858](https://github.com/containous/traefik/pull/4858) by [ldez](https://github.com/ldez)) +- **[tcp]** Remove first byte wait when tcp catches all ([#4938](https://github.com/containous/traefik/pull/4938) by [juliens](https://github.com/juliens)) +- **[tcp]** On client CloseWrite, do CloseWrite instead of Close for backend ([#5366](https://github.com/containous/traefik/pull/5366) by [juliens](https://github.com/juliens)) +- **[tls]** Fix panic in TLS stores handling ([#4997](https://github.com/containous/traefik/pull/4997) by [juliens](https://github.com/juliens)) +- **[webui]** Rest provider icon in the webui ([#5261](https://github.com/containous/traefik/pull/5261) by [mmatur](https://github.com/mmatur)) +- **[webui]** Web UI graph names. ([#5389](https://github.com/containous/traefik/pull/5389) by [ldez](https://github.com/ldez)) +- **[webui]** fix: passHostHeader in the webUI. ([#5369](https://github.com/containous/traefik/pull/5369) by [ldez](https://github.com/ldez)) +- Fix trailing slash with check new version ([#5266](https://github.com/containous/traefik/pull/5266) by [mmatur](https://github.com/mmatur)) +- Ensure WaitGroup.Done() is always called ([#5026](https://github.com/containous/traefik/pull/5026) by [bsdelf](https://github.com/bsdelf)) +- Clean files during tests. ([#4607](https://github.com/containous/traefik/pull/4607) by [ldez](https://github.com/ldez)) + +**Documentation:** +- **[acme,docker]** Removed extra colon before the 8080 docker port ([#5209](https://github.com/containous/traefik/pull/5209) by [fairwood136](https://github.com/fairwood136)) +- **[acme,docker]** Add a docker-compose & let's encrypt user-guide ([#5121](https://github.com/containous/traefik/pull/5121) by [pbenefice](https://github.com/pbenefice)) +- **[acme,docker]** Synchronize documentation ([#4571](https://github.com/containous/traefik/pull/4571) by [juliens](https://github.com/juliens)) +- **[acme,k8s,k8s/crd]** Full ACME+CRD example ([#4652](https://github.com/containous/traefik/pull/4652) by [mpl](https://github.com/mpl)) +- **[acme,k8s/crd]** Fix: CRD user guide ([#5244](https://github.com/containous/traefik/pull/5244) by [ldez](https://github.com/ldez)) +- **[acme,tls]** docs: rewrite of the HTTPS and TLS section ([#4980](https://github.com/containous/traefik/pull/4980) by [mpl](https://github.com/mpl)) +- **[acme]** Lets encrypt documentation typo ([#5127](https://github.com/containous/traefik/pull/5127) by [juliens](https://github.com/juliens)) +- **[acme]** Use the same case every where for entryPoints. ([#4764](https://github.com/containous/traefik/pull/4764) by [ldez](https://github.com/ldez)) +- **[acme]** doc/crd-acme: specify required kubectl version ([#5015](https://github.com/containous/traefik/pull/5015) by [mpl](https://github.com/mpl)) +- **[acme]** Enhance manual dnsChallenge documentation ([#4636](https://github.com/containous/traefik/pull/4636) by [ntaranov](https://github.com/ntaranov)) +- **[acme]** Fix error in the documentation for CLI configuration example ([#5392](https://github.com/containous/traefik/pull/5392) by [MycTl](https://github.com/MycTl)) +- **[acme]** Add note about ACME renewal ([#4860](https://github.com/containous/traefik/pull/4860) by [dtomcej](https://github.com/dtomcej)) +- **[acme]** Fix acme example ([#5130](https://github.com/containous/traefik/pull/5130) by [jamct](https://github.com/jamct)) +- **[acme]** Rename Docker_Acme.md to Readme.md ([#4025](https://github.com/containous/traefik/pull/4025) by [vineetvermait](https://github.com/vineetvermait)) +- **[acme]** Enhance acme page. ([#4611](https://github.com/containous/traefik/pull/4611) by [ldez](https://github.com/ldez)) +- **[acme]** fix: some DNS provider link. ([#3637](https://github.com/containous/traefik/pull/3637) by [ldez](https://github.com/ldez)) +- **[docker,marathon]** Update Dynamic Configuration Reference for both Docker and Marathon ([#5100](https://github.com/containous/traefik/pull/5100) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[docker]** Remove traefik.port from documentation ([#4886](https://github.com/containous/traefik/pull/4886) by [ldez](https://github.com/ldez)) +- **[docker]** Fix two minor nits in Traefik 2.0 docs ([#4692](https://github.com/containous/traefik/pull/4692) by [cfra](https://github.com/cfra)) +- **[docker]** Fix Getting started ([#4646](https://github.com/containous/traefik/pull/4646) by [mmatur](https://github.com/mmatur)) +- **[docker]** docker-compose examples ([#4642](https://github.com/containous/traefik/pull/4642) by [karnthis](https://github.com/karnthis)) +- **[docker]** Clarify docs with labels in Swarm Mode ([#4847](https://github.com/containous/traefik/pull/4847) by [mikesir87](https://github.com/mikesir87)) +- **[file]** Update the file provider documentation ([#4588](https://github.com/containous/traefik/pull/4588) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[k8s,k8s/crd]** k8s static configuration explanation ([#4767](https://github.com/containous/traefik/pull/4767) by [ldez](https://github.com/ldez)) +- **[k8s,k8s/crd]** doc: kubernetes CRD provider ([#4620](https://github.com/containous/traefik/pull/4620) by [mpl](https://github.com/mpl)) +- **[k8s,k8s/ingress]** Add documentation about Kubernetes Ingress provider ([#5112](https://github.com/containous/traefik/pull/5112) by [mpl](https://github.com/mpl)) +- **[k8s/crd]** user guide: fix a mistake in the deployment definition ([#5096](https://github.com/containous/traefik/pull/5096) by [ldez](https://github.com/ldez)) +- **[k8s]** Fix typo in the CRD documentation ([#4902](https://github.com/containous/traefik/pull/4902) by [llussy](https://github.com/llussy)) +- **[marathon]** Enhance Marathon documentation ([#4776](https://github.com/containous/traefik/pull/4776) by [ldez](https://github.com/ldez)) +- **[middleware,k8s,k8s/crd]** Fix typo: middleware -> middlewares. ([#4781](https://github.com/containous/traefik/pull/4781) by [ldez](https://github.com/ldez)) +- **[middleware,k8s/crd]** doc: fix middleware names for CRD. ([#4966](https://github.com/containous/traefik/pull/4966) by [ldez](https://github.com/ldez)) +- **[middleware,provider]** fix the documentation about middleware labels. ([#4888](https://github.com/containous/traefik/pull/4888) by [ldez](https://github.com/ldez)) +- **[middleware]** Fix Kubernetes Docs for Middlewares ([#4943](https://github.com/containous/traefik/pull/4943) by [HurricanKai](https://github.com/HurricanKai)) +- **[middleware]** Adds a reference to the middleware overview. ([#4824](https://github.com/containous/traefik/pull/4824) by [ldez](https://github.com/ldez)) +- **[middleware]** docker-compose labels require $'s to be escaped ([#5225](https://github.com/containous/traefik/pull/5225) by [Makeshift](https://github.com/Makeshift)) +- **[middleware]** Fix doc about removing headers ([#4708](https://github.com/containous/traefik/pull/4708) by [mpl](https://github.com/mpl)) +- **[middleware]** Remove invalid commas. ([#4706](https://github.com/containous/traefik/pull/4706) by [ldez](https://github.com/ldez)) +- **[middleware]** Adds middlewares examples for k8s. ([#4713](https://github.com/containous/traefik/pull/4713) by [ldez](https://github.com/ldez)) +- **[middleware]** Update the middleware documentation ([#4729](https://github.com/containous/traefik/pull/4729) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[middleware]** fix: stripPrefixRegex documentation. ([#5273](https://github.com/containous/traefik/pull/5273) by [ldez](https://github.com/ldez)) +- **[middleware]** Correct typo in documentation on rate limiting ([#4939](https://github.com/containous/traefik/pull/4939) by [ableuler](https://github.com/ableuler)) +- **[middleware]** Improve middleware documentation. ([#5003](https://github.com/containous/traefik/pull/5003) by [ldez](https://github.com/ldez)) +- **[middleware]** Enhance middleware examples. ([#4680](https://github.com/containous/traefik/pull/4680) by [ldez](https://github.com/ldez)) +- **[middleware]** docker-compose basic auth needs double dollar signs ([#4831](https://github.com/containous/traefik/pull/4831) by [muhlemmer](https://github.com/muhlemmer)) +- **[middleware]** Fixed a typo in label. ([#5128](https://github.com/containous/traefik/pull/5128) by [jamct](https://github.com/jamct)) +- **[middleware]** Review documentation ([#4798](https://github.com/containous/traefik/pull/4798) by [ldez](https://github.com/ldez)) +- **[middleware]** Kubernetes CRD documentation fixes ([#4971](https://github.com/containous/traefik/pull/4971) by [orhanhenrik](https://github.com/orhanhenrik)) +- **[middleware]** compress link fixed ([#4817](https://github.com/containous/traefik/pull/4817) by [gato](https://github.com/gato)) +- **[middleware]** Fix typo in forwardAuth middleware documentation ([#4638](https://github.com/containous/traefik/pull/4638) by [AkeemMcLennon](https://github.com/AkeemMcLennon)) +- **[middleware]** change doc references to scheme[Rr]edirect -> redirect[Ss]cheme ([#4959](https://github.com/containous/traefik/pull/4959) by [topiaruss](https://github.com/topiaruss)) +- **[middleware]** Update headers middleware docs for kubernetes crd ([#4955](https://github.com/containous/traefik/pull/4955) by [orhanhenrik](https://github.com/orhanhenrik)) +- **[middleware]** Fix strip prefix documentation ([#4829](https://github.com/containous/traefik/pull/4829) by [mmatur](https://github.com/mmatur)) +- **[provider]** Improve providers documentation. ([#5050](https://github.com/containous/traefik/pull/5050) by [ldez](https://github.com/ldez)) +- **[rancher]** fix: Rancher documentation. ([#4818](https://github.com/containous/traefik/pull/4818) by [ldez](https://github.com/ldez)) +- **[rancher]** Specify that Rancher provider is for 1.x only ([#4923](https://github.com/containous/traefik/pull/4923) by [bradjones1](https://github.com/bradjones1)) +- **[server]** Add gRPC user guide ([#5042](https://github.com/containous/traefik/pull/5042) by [ldez](https://github.com/ldez)) +- **[tcp]** Use rule HostSNI in documentation ([#4592](https://github.com/containous/traefik/pull/4592) by [bbinet](https://github.com/bbinet)) +- **[tls]** fix: typo in routing example. ([#4849](https://github.com/containous/traefik/pull/4849) by [ldez](https://github.com/ldez)) +- **[tracing]** Improve tracing documentation ([#5102](https://github.com/containous/traefik/pull/5102) by [mmatur](https://github.com/mmatur)) +- **[tracing]** Fix typo in tracing docs ([#4737](https://github.com/containous/traefik/pull/4737) by [timoschwarzer](https://github.com/timoschwarzer)) +- **[webui]** change docs and adjust dashboard for v2 alpha ([#4632](https://github.com/containous/traefik/pull/4632) by [SantoDE](https://github.com/SantoDE)) +- doc: improve examples. ([#5132](https://github.com/containous/traefik/pull/5132) by [ldez](https://github.com/ldez)) +- Fixed readme misspelling ([#4882](https://github.com/containous/traefik/pull/4882) by [antondalgren](https://github.com/antondalgren)) +- Prepare release v2.0.0-rc2 ([#5293](https://github.com/containous/traefik/pull/5293) by [ldez](https://github.com/ldez)) +- Fix typos in documentation ([#4884](https://github.com/containous/traefik/pull/4884) by [michael-k](https://github.com/michael-k)) +- Fixed spelling typo ([#4848](https://github.com/containous/traefik/pull/4848) by [mikesir87](https://github.com/mikesir87)) +- Enhance the Retry Middleware Documentation ([#5298](https://github.com/containous/traefik/pull/5298) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Clarification of the correct pronunciation of the word "Traefik" ([#4834](https://github.com/containous/traefik/pull/4834) by [ylamlum-g4m](https://github.com/ylamlum-g4m)) +- Improve the "reading path" for new contributors ([#4908](https://github.com/containous/traefik/pull/4908) by [dduportal](https://github.com/dduportal)) +- Fix some documentation issues ([#5286](https://github.com/containous/traefik/pull/5286) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Entry points CLI description. ([#4896](https://github.com/containous/traefik/pull/4896) by [ldez](https://github.com/ldez)) +- Add Mathieu Lonjaret to maintainers ([#4950](https://github.com/containous/traefik/pull/4950) by [emilevauge](https://github.com/emilevauge)) +- Prepare release v2.0.0-alpha5 ([#4967](https://github.com/containous/traefik/pull/4967) by [ldez](https://github.com/ldez)) +- Minor fix in documentation ([#4811](https://github.com/containous/traefik/pull/4811) by [mmatur](https://github.com/mmatur)) +- Prepare release v2.0.0-alpha6. ([#4975](https://github.com/containous/traefik/pull/4975) by [ldez](https://github.com/ldez)) +- Fix a typo in documentation ([#4794](https://github.com/containous/traefik/pull/4794) by [groovytron](https://github.com/groovytron)) +- Prepare release v2.0.0-alpha4. ([#4788](https://github.com/containous/traefik/pull/4788) by [ldez](https://github.com/ldez)) +- Remove dumpcerts.sh ([#4783](https://github.com/containous/traefik/pull/4783) by [ldez](https://github.com/ldez)) +- Base of the migration guide ([#5263](https://github.com/containous/traefik/pull/5263) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Prepare release v2.0.0-alpha7 ([#5001](https://github.com/containous/traefik/pull/5001) by [ldez](https://github.com/ldez)) +- misc documentation fixes ([#5302](https://github.com/containous/traefik/pull/5302) by [mpl](https://github.com/mpl)) +- Fix some minors errors on the documentation ([#4664](https://github.com/containous/traefik/pull/4664) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Adds a note in traefik.sample.toml ([#4757](https://github.com/containous/traefik/pull/4757) by [ldez](https://github.com/ldez)) +- Prepare release v2.0.0-rc1 ([#5252](https://github.com/containous/traefik/pull/5252) by [ldez](https://github.com/ldez)) +- Use the same case everywhere ([#5043](https://github.com/containous/traefik/pull/5043) by [ldez](https://github.com/ldez)) +- Improve the Documentation with a Reference Section ([#4714](https://github.com/containous/traefik/pull/4714) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Prepare release v2.0.0-alpha8 ([#5049](https://github.com/containous/traefik/pull/5049) by [ldez](https://github.com/ldez)) +- Add a basic Traefik install guide ([#5117](https://github.com/containous/traefik/pull/5117) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- AML indent for domains under TLS documentation section ([#5173](https://github.com/containous/traefik/pull/5173) by [edvincent](https://github.com/edvincent)) +- Update to v2.0 readme links ([#4700](https://github.com/containous/traefik/pull/4700) by [karnthis](https://github.com/karnthis)) +- Prepare release v2.0.0-alpha3. ([#4693](https://github.com/containous/traefik/pull/4693) by [ldez](https://github.com/ldez)) +- Misc documentation fixes ([#5307](https://github.com/containous/traefik/pull/5307) by [ldez](https://github.com/ldez)) +- Update restrictions in the documentation. ([#5270](https://github.com/containous/traefik/pull/5270) by [ldez](https://github.com/ldez)) +- Prepare release v2.0.0-rc3 ([#5343](https://github.com/containous/traefik/pull/5343) by [ldez](https://github.com/ldez)) +- Fix typos in docs ([#4662](https://github.com/containous/traefik/pull/4662) by [SeMeKh](https://github.com/SeMeKh)) +- Update traefik.sample.toml ([#4657](https://github.com/containous/traefik/pull/4657) by [ldez](https://github.com/ldez)) +- fix: services configuration documentation. ([#5359](https://github.com/containous/traefik/pull/5359) by [ldez](https://github.com/ldez)) +- Remove old links in readme ([#4651](https://github.com/containous/traefik/pull/4651) by [ldez](https://github.com/ldez)) +- fix a service with one server .yaml example ([#5373](https://github.com/containous/traefik/pull/5373) by [zaverden](https://github.com/zaverden)) +- Prepare release v2.0.0-rc4 ([#5384](https://github.com/containous/traefik/pull/5384) by [ldez](https://github.com/ldez)) +- Fix dead maintainers link on the README.md ([#4639](https://github.com/containous/traefik/pull/4639) by [benjaminch](https://github.com/benjaminch)) +- Prepare release v2.0.0-beta1 ([#5129](https://github.com/containous/traefik/pull/5129) by [ldez](https://github.com/ldez)) +- Fix typo in documentation ([#5386](https://github.com/containous/traefik/pull/5386) by [adrienbrignon](https://github.com/adrienbrignon)) +- Prepare release v2.0.0-alpha2 ([#4635](https://github.com/containous/traefik/pull/4635) by [ldez](https://github.com/ldez)) +- Fix malformed rule ([#5133](https://github.com/containous/traefik/pull/5133) by [dtomcej](https://github.com/dtomcej)) +- Improve various parts of the documentation. ([#4996](https://github.com/containous/traefik/pull/4996) by [ldez](https://github.com/ldez)) +- Documentation Revamp ([#4475](https://github.com/containous/traefik/pull/4475) by [geraldcroes](https://github.com/geraldcroes)) +- Adds a maintainer's page into the documentation. ([#4614](https://github.com/containous/traefik/pull/4614) by [ldez](https://github.com/ldez)) +- Add Gerald, Jean-Baptiste and Damien to maintainers ([#3982](https://github.com/containous/traefik/pull/3982) by [emilevauge](https://github.com/emilevauge)) +- fix broken links in readme.md ([#3967](https://github.com/containous/traefik/pull/3967) by [AndrewSav](https://github.com/AndrewSav)) +- Add master overhaul notice ([#3961](https://github.com/containous/traefik/pull/3961) by [emilevauge](https://github.com/emilevauge)) +- Complete maintainers processes ([#3696](https://github.com/containous/traefik/pull/3696) by [mmatur](https://github.com/mmatur)) +- Complete maintainers processes ([#3681](https://github.com/containous/traefik/pull/3681) by [emilevauge](https://github.com/emilevauge)) +- Prepare release v2.0.0-alpha1 ([#4617](https://github.com/containous/traefik/pull/4617) by [ldez](https://github.com/ldez)) + +**Misc:** +- Cherry pick v1.7 into v2.0 ([#5341](https://github.com/containous/traefik/pull/5341) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Cherry pick v1.7 into v2.0 ([#5192](https://github.com/containous/traefik/pull/5192) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into v2.0 ([#5115](https://github.com/containous/traefik/pull/5115) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Cherry pick v1.7 into v2.0 ([#4948](https://github.com/containous/traefik/pull/4948) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into v2.0 ([#4823](https://github.com/containous/traefik/pull/4823) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into v2.0 ([#4787](https://github.com/containous/traefik/pull/4787) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into v2.0 ([#4695](https://github.com/containous/traefik/pull/4695) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Merge v2.0.0-rc1 into master ([#5253](https://github.com/containous/traefik/pull/5253) by [ldez](https://github.com/ldez)) +- Merge branch v2.0 into master ([#5180](https://github.com/containous/traefik/pull/5180) by [ldez](https://github.com/ldez)) +- Merge v2.0.0-alpha8 into master ([#5055](https://github.com/containous/traefik/pull/5055) by [ldez](https://github.com/ldez)) +- Merge current v2.0.0-alpha into master ([#5022](https://github.com/containous/traefik/pull/5022) by [ldez](https://github.com/ldez)) +- Merge v2.0.0-alpha6 into master ([#4984](https://github.com/containous/traefik/pull/4984) by [ldez](https://github.com/ldez)) +- Merge v2.0.0-alpha4 into master ([#4789](https://github.com/containous/traefik/pull/4789) by [ldez](https://github.com/ldez)) +- Merge v2.0.0-alpha3 into master ([#4694](https://github.com/containous/traefik/pull/4694) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4565](https://github.com/containous/traefik/pull/4565) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- Cherry pick v1.7 into master ([#4511](https://github.com/containous/traefik/pull/4511) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4492](https://github.com/containous/traefik/pull/4492) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4440](https://github.com/containous/traefik/pull/4440) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4365](https://github.com/containous/traefik/pull/4365) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4303](https://github.com/containous/traefik/pull/4303) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4271](https://github.com/containous/traefik/pull/4271) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4268](https://github.com/containous/traefik/pull/4268) by [ldez](https://github.com/ldez)) +- Cherry pick v1.7 into master ([#4229](https://github.com/containous/traefik/pull/4229) by [juliens](https://github.com/juliens)) +- Cherry pick v1.7 into master ([#4206](https://github.com/containous/traefik/pull/4206) by [ldez](https://github.com/ldez)) +- Merge v1.7.4 into master ([#4137](https://github.com/containous/traefik/pull/4137) by [ldez](https://github.com/ldez)) +- Merge v1.7.3 into master ([#4046](https://github.com/containous/traefik/pull/4046) by [ldez](https://github.com/ldez)) +- Merge current v1.7 into master ([#3992](https://github.com/containous/traefik/pull/3992) by [ldez](https://github.com/ldez)) +- Merge v1.7.2 into master ([#3983](https://github.com/containous/traefik/pull/3983) by [ldez](https://github.com/ldez)) +- Merge v1.7.0 into master ([#3925](https://github.com/containous/traefik/pull/3925) by [ldez](https://github.com/ldez)) +- Merge v1.7.0-rc5 into master ([#3903](https://github.com/containous/traefik/pull/3903) by [ldez](https://github.com/ldez)) +- Merge v1.7.0-rc4 into master ([#3867](https://github.com/containous/traefik/pull/3867) by [ldez](https://github.com/ldez)) +- Merge v1.7.0-rc2 into master ([#3634](https://github.com/containous/traefik/pull/3634) by [ldez](https://github.com/ldez)) + ## [v2.0.0-rc4](https://github.com/containous/traefik/tree/v2.0.0-rc4) (2019-09-13) [All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc3...v2.0.0-rc4) From e0a1592e6e358bc01f4b89035b7ab3aeb073df2d Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Tue, 17 Sep 2019 16:12:04 +0200 Subject: [PATCH 18/19] Fix case-sensitive header in websocket --- pkg/server/service/proxy.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/server/service/proxy.go b/pkg/server/service/proxy.go index a305fbb1d..c19bd124f 100644 --- a/pkg/server/service/proxy.go +++ b/pkg/server/service/proxy.go @@ -57,6 +57,11 @@ func buildProxy(passHostHeader bool, responseForwarding *dynamic.ResponseForward outReq.Host = outReq.URL.Host } + // Even if the websocket RFC says that headers should be case-insensitive, + // some servers need Sec-WebSocket-Key to be case-sensitive. + // https://tools.ietf.org/html/rfc6455#page-20 + outReq.Header["Sec-WebSocket-Key"] = outReq.Header["Sec-Websocket-Key"] + delete(outReq.Header, "Sec-Websocket-Key") }, Transport: defaultRoundTripper, FlushInterval: time.Duration(flushInterval), From 7f0c9c239ea2c46ae6842255ed7f736d80c28320 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 17 Sep 2019 17:12:04 +0200 Subject: [PATCH 19/19] fix: CNAME of the docs. --- docs/{ => content}/CNAME | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{ => content}/CNAME (100%) diff --git a/docs/CNAME b/docs/content/CNAME similarity index 100% rename from docs/CNAME rename to docs/content/CNAME