Merge branch v2.1 into master

This commit is contained in:
Fernandez Ludovic 2020-02-10 16:03:39 +01:00
commit aa21351d0d
76 changed files with 392 additions and 395 deletions

View file

@ -1,3 +1,24 @@
## [v2.1.4](https://github.com/containous/traefik/tree/v2.1.4) (2020-02-06)
[All Commits](https://github.com/containous/traefik/compare/v2.1.3...v2.1.4)
**Bug fixes:**
- **[acme,logs]** Improvement of the certificates resolvers logs ([#6225](https://github.com/containous/traefik/pull/6225) by [ldez](https://github.com/ldez))
- **[acme]** Fix kubernetes providers shutdown and clean safe.Pool ([#6244](https://github.com/containous/traefik/pull/6244) by [juliens](https://github.com/juliens))
- **[authentication,middleware]** don't create http client for each request in forwardAuth middleware ([#6267](https://github.com/containous/traefik/pull/6267) by [juliens](https://github.com/juliens))
- **[k8s,k8s/ingress]** Allow wildcard hosts in ingress provider ([#6251](https://github.com/containous/traefik/pull/6251) by [dtomcej](https://github.com/dtomcej))
- **[logs,tls]** Properly purge default certificate from stores before logging ([#6281](https://github.com/containous/traefik/pull/6281) by [dtomcej](https://github.com/dtomcej))
- **[middleware]** use provider-qualified name when recursing for chain ([#6233](https://github.com/containous/traefik/pull/6233) by [mpl](https://github.com/mpl))
**Documentation:**
- **[acme,cli]** Documentation fix for acme.md CLI ([#6262](https://github.com/containous/traefik/pull/6262) by [altano](https://github.com/altano))
- **[acme,k8s/crd]** Add missing certResolver in IngressRoute examples. ([#6265](https://github.com/containous/traefik/pull/6265) by [ldez](https://github.com/ldez))
- **[k8s]** fix a typo ([#6279](https://github.com/containous/traefik/pull/6279) by [silenceshell](https://github.com/silenceshell))
- **[middleware]** Minor documentation tweaks. ([#6218](https://github.com/containous/traefik/pull/6218) by [stevegroom](https://github.com/stevegroom))
- Correct a trivial spelling mistake in the documentation. ([#6269](https://github.com/containous/traefik/pull/6269) by [nepella](https://github.com/nepella))
- Update install-traefik.md ([#6260](https://github.com/containous/traefik/pull/6260) by [bitfactory-sander-lissenburg](https://github.com/bitfactory-sander-lissenburg))
- doc: use the same entry point name everywhere ([#6219](https://github.com/containous/traefik/pull/6219) by [ldez](https://github.com/ldez))
- readme: update links to use HTTPS ([#6274](https://github.com/containous/traefik/pull/6274) by [imba-tjd](https://github.com/imba-tjd))
## [v2.1.3](https://github.com/containous/traefik/tree/v2.1.3) (2020-01-21) ## [v2.1.3](https://github.com/containous/traefik/tree/v2.1.3) (2020-01-21)
[All Commits](https://github.com/containous/traefik/compare/v2.1.2...v2.1.3) [All Commits](https://github.com/containous/traefik/compare/v2.1.2...v2.1.3)

View file

@ -5,7 +5,7 @@
[![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/traefik) [![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/traefik)
[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://docs.traefik.io) [![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://docs.traefik.io)
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](http://goreportcard.com/report/containous/traefik) [![Go Report Card](https://goreportcard.com/badge/containous/traefik)](https://goreportcard.com/report/containous/traefik)
[![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik) [![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md)
[![Join the community support forum at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/) [![Join the community support forum at https://community.containo.us/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.containo.us/)
@ -89,7 +89,7 @@ You can access the simple HTML frontend of Traefik.
You can find the complete documentation of Traefik v2 at [https://docs.traefik.io](https://docs.traefik.io). You can find the complete documentation of Traefik v2 at [https://docs.traefik.io](https://docs.traefik.io).
If you are using Traefik v1, you can find the complete documentation at [https://docs.traefik.io/v1.7/](https://docs.traefik.io/v1.7/) If you are using Traefik v1, you can find the complete documentation at [https://docs.traefik.io/v1.7/](https://docs.traefik.io/v1.7/).
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io). A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
@ -122,7 +122,7 @@ git clone https://github.com/containous/traefik
## Introductory Videos ## Introductory Videos
You can find high level and deep dive videos on [videos.containo.us](https://videos.containo.us) You can find high level and deep dive videos on [videos.containo.us](https://videos.containo.us).
## Maintainers ## Maintainers
@ -138,16 +138,16 @@ By participating in this project, you agree to abide by its terms.
## Release Cycle ## Release Cycle
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month. - We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0) - Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only) - Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out) Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out).
We use [Semantic Versioning](http://semver.org/) We use [Semantic Versioning](https://semver.org/).
## Mailing lists ## Mailing Lists
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news) - General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news).
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security). - Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
## Credits ## Credits
@ -156,5 +156,5 @@ Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on t
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license. Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn). Traefik's logo was inspired by the gopher stickers made by [Takuya Ueda](https://twitter.com/tenntenn).
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/). The original Go gopher was designed by [Renee French](https://reneefrench.blogspot.com/).

View file

@ -267,14 +267,18 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
} }
if err := providerAggregator.AddProvider(p); err != nil { if err := providerAggregator.AddProvider(p); err != nil {
log.WithoutContext().Errorf("Unable to add ACME provider to the providers list: %v", err) log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
continue continue
} }
p.SetTLSManager(tlsManager) p.SetTLSManager(tlsManager)
if p.TLSChallenge != nil { if p.TLSChallenge != nil {
tlsManager.TLSAlpnGetter = p.GetTLSALPNCertificate tlsManager.TLSAlpnGetter = p.GetTLSALPNCertificate
} }
p.SetConfigListenerChan(make(chan dynamic.Configuration)) p.SetConfigListenerChan(make(chan dynamic.Configuration))
resolvers = append(resolvers, p) resolvers = append(resolvers, p)
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

After

Width:  |  Height:  |  Size: 284 KiB

View file

@ -3,11 +3,11 @@
A Quick Guide for Efficient Contributions A Quick Guide for Efficient Contributions
{: .subtitle } {: .subtitle }
So you've decide to improve Traefik? So you've decided to improve Traefik?
Thank You! Thank You!
Now the last step is to submit your Pull Request in a way that makes sure it gets the attention it deserves. Now the last step is to submit your Pull Request in a way that makes sure it gets the attention it deserves.
Let's go though the classic pitfalls to make sure everything is right. Let's go through the classic pitfalls to make sure everything is right.
## Title ## Title
@ -36,7 +36,7 @@ Help the readers focus on what matters, and help them understand the structure o
- Add tests. - Add tests.
- Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial). - Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial).
!!! note "third-party dependencies" !!! note "Third-Party Dependencies"
If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated. If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated.

View file

@ -23,7 +23,7 @@ For more details, go to the [Docker provider documentation](../providers/docker.
* Prefer a fixed version than the latest that could be an unexpected version. * Prefer a fixed version than the latest that could be an unexpected version.
ex: `traefik:v2.0.0` ex: `traefik:v2.0.0`
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine). * Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
* All the orchestrator using docker images could fetch the official Traefik docker image. * Any orchestrator using docker images can fetch the official Traefik docker image.
## Use the Helm Chart ## Use the Helm Chart

View file

@ -56,7 +56,7 @@ Please check the [configuration examples below](#configuration-examples) for mor
[entryPoints.web] [entryPoints.web]
address = ":80" address = ":80"
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[certificatesResolvers.le.acme] [certificatesResolvers.le.acme]
@ -72,7 +72,7 @@ Please check the [configuration examples below](#configuration-examples) for mor
web: web:
address: ":80" address: ":80"
web-secure: websecure:
address: ":443" address: ":443"
certificatesResolvers: certificatesResolvers:
@ -196,7 +196,7 @@ when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge
[entryPoints.web] [entryPoints.web]
address = ":80" address = ":80"
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[certificatesResolvers.le.acme] [certificatesResolvers.le.acme]
@ -210,7 +210,7 @@ when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge
web: web:
address: ":80" address: ":80"
web-secure: websecure:
address: ":443" address: ":443"
certificatesResolvers: certificatesResolvers:
@ -379,7 +379,7 @@ certificatesResolvers:
```bash tab="CLI" ```bash tab="CLI"
# ... # ...
--certificatesResolvers.le.acme.dnsChallenge.resolvers:=1.1.1.1:53,8.8.8.8:53 --certificatesResolvers.le.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
``` ```
#### Wildcard Domains #### Wildcard Domains

View file

@ -22,7 +22,6 @@ deploy:
``` ```
```yaml tab="Kubernetes" ```yaml tab="Kubernetes"
---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:

View file

@ -18,7 +18,6 @@ deploy:
``` ```
```yaml tab="Kubernetes" ```yaml tab="Kubernetes"
---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
@ -32,7 +31,8 @@ spec:
services: services:
- name: blog - name: blog
port: 8080 port: 8080
tls: {} tls:
certresolver: le
``` ```
```json tab="Marathon" ```json tab="Marathon"
@ -58,7 +58,7 @@ labels:
[http.routers.blog] [http.routers.blog]
rule = "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)" rule = "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)"
[http.routers.blog.tls] [http.routers.blog.tls]
certResolver = "le" # From static configuration certResolver = "le"
``` ```
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"

View file

@ -18,7 +18,6 @@ deploy:
``` ```
```yaml tab="Kubernetes" ```yaml tab="Kubernetes"
---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
@ -32,7 +31,8 @@ spec:
services: services:
- name: blog - name: blog
port: 8080 port: 8080
tls: {} tls:
certresolver: le
``` ```
```json tab="Marathon" ```json tab="Marathon"
@ -55,10 +55,10 @@ labels:
```toml tab="Single Domain" ```toml tab="Single Domain"
## Dynamic configuration ## Dynamic configuration
[http.routers] [http.routers]
[http.routers.blog] [http.routers.blog]
rule = "Host(`company.com`) && Path(`/blog`)" rule = "Host(`company.com`) && Path(`/blog`)"
[http.routers.blog.tls] [http.routers.blog.tls]
certResolver = "le" # From static configuration certResolver = "le"
``` ```
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"

View file

@ -20,4 +20,9 @@ Developing Traefik, our main goal is to make it simple to use, and we're sure yo
!!! info !!! info
If you're a business running critical services behind Traefik, know that [Containous](https://containo.us), the company that sponsors Traefik's development, can provide [commercial support](https://info.containo.us/commercial-services) and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik. Join our user friendly and active [Community Forum](https://community.containo.us) to discuss, learn, and connect with the traefik community.
If you're a business running critical services behind Traefik,
know that [Containous](https://containo.us), the company that sponsors Traefik's development,
can provide [commercial support](https://info.containo.us/commercial-services)
and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik.

View file

@ -5,9 +5,9 @@ Tweaking the Request
![Overview](../assets/img/middleware/overview.png) ![Overview](../assets/img/middleware/overview.png)
Attached to the routers, pieces of middleware are a mean of tweaking the requests before they are sent to your [service](../routing/services/index.md) (or before the answer from the services are sent to the clients). Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your [service](../routing/services/index.md) (or before the answer from the services are sent to the clients).
There are many different available middlewares in Traefik, some can modify the request, the headers, some are in charge of redirections, some add authentication, and so on. There are several available middleware in Traefik, some can modify the request, the headers, some are in charge of redirections, some add authentication, and so on.
Pieces of middleware can be combined in chains to fit every scenario. Pieces of middleware can be combined in chains to fit every scenario.
@ -130,7 +130,7 @@ http:
## Provider Namespace ## Provider Namespace
When you declare a middleware, it lives in its provider namespace. When you declare a middleware, it lives in its provider's namespace.
For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker provider namespace. For example, if you declare a middleware using a Docker label, under the hoods, it will reside in the docker provider namespace.
If you use multiple providers and wish to reference a middleware declared in another provider If you use multiple providers and wish to reference a middleware declared in another provider

View file

@ -190,17 +190,17 @@ TLS parameters used to be specified in the static configuration, as an entryPoin
With Traefik v2, a new dynamic TLS section at the root contains all the desired TLS configurations. With Traefik v2, a new dynamic TLS section at the root contains all the desired TLS configurations.
Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one of the [TLS configurations](../https/tls.md) defined at the root, hence defining the [TLS configuration](../https/tls.md) for that router. Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one of the [TLS configurations](../https/tls.md) defined at the root, hence defining the [TLS configuration](../https/tls.md) for that router.
!!! example "TLS on web-secure entryPoint becomes TLS option on Router-1" !!! example "TLS on websecure entryPoint becomes TLS option on Router-1"
!!! info "v1" !!! info "v1"
```toml tab="File (TOML)" ```toml tab="File (TOML)"
# static configuration # static configuration
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[entryPoints.web-secure.tls] [entryPoints.websecure.tls]
minVersion = "VersionTLS12" minVersion = "VersionTLS12"
cipherSuites = [ cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
@ -210,13 +210,13 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
] ]
[[entryPoints.web-secure.tls.certificates]] [[entryPoints.websecure.tls.certificates]]
certFile = "path/to/my.cert" certFile = "path/to/my.cert"
keyFile = "path/to/my.key" keyFile = "path/to/my.key"
``` ```
```bash tab="CLI" ```bash tab="CLI"
--entryPoints='Name:web-secure Address::443 TLS:path/to/my.cert,path/to/my.key TLS.MinVersion:VersionTLS12 TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' --entryPoints='Name:websecure Address::443 TLS:path/to/my.cert,path/to/my.key TLS.MinVersion:VersionTLS12 TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
``` ```
!!! info "v2" !!! info "v2"
@ -818,32 +818,32 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
```toml tab="File (TOML)" ```toml tab="File (TOML)"
# static configuration # static configuration
defaultEntryPoints = ["web-secure","web"] defaultEntryPoints = ["websecure","web"]
[entryPoints.web] [entryPoints.web]
address = ":80" address = ":80"
[entryPoints.web.redirect] [entryPoints.web.redirect]
entryPoint = "webs" entryPoint = "webs"
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[entryPoints.https.tls] [entryPoints.https.tls]
[acme] [acme]
email = "your-email-here@my-awesome-app.org" email = "your-email-here@my-awesome-app.org"
storage = "acme.json" storage = "acme.json"
entryPoint = "web-secure" entryPoint = "websecure"
onHostRule = true onHostRule = true
[acme.httpChallenge] [acme.httpChallenge]
entryPoint = "web" entryPoint = "web"
``` ```
```bash tab="CLI" ```bash tab="CLI"
--defaultentrypoints=web-secure,web --defaultentrypoints=websecure,web
--entryPoints=Name:web Address::80 Redirect.EntryPoint:web-secure --entryPoints=Name:web Address::80 Redirect.EntryPoint:websecure
--entryPoints=Name:web-secure Address::443 TLS --entryPoints=Name:websecure Address::443 TLS
--acme.email=your-email-here@my-awesome-app.org --acme.email=your-email-here@my-awesome-app.org
--acme.storage=acme.json --acme.storage=acme.json
--acme.entryPoint=web-secure --acme.entryPoint=websecure
--acme.onHostRule=true --acme.onHostRule=true
--acme.httpchallenge.entrypoint=http --acme.httpchallenge.entrypoint=http
``` ```
@ -856,7 +856,7 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
[entryPoints.web] [entryPoints.web]
address = ":80" address = ":80"
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[certificatesResolvers.sample.acme] [certificatesResolvers.sample.acme]
@ -872,7 +872,7 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
web: web:
address: ":80" address: ":80"
web-secure: websecure:
address: ":443" address: ":443"
certificatesResolvers: certificatesResolvers:
@ -1069,7 +1069,7 @@ Each root item has been moved to a related section or removed.
providersThrottleDuration = "2s" providersThrottleDuration = "2s"
AllowMinWeightZero = true AllowMinWeightZero = true
debug = true debug = true
defaultEntryPoints = ["web", "web-secure"] defaultEntryPoints = ["web", "websecure"]
keepTrailingSlash = false keepTrailingSlash = false
``` ```
@ -1083,7 +1083,7 @@ Each root item has been moved to a related section or removed.
--providersthrottleduration=2s --providersthrottleduration=2s
--allowminweightzero=true --allowminweightzero=true
--debug=true --debug=true
--defaultentrypoints=web,web-secure --defaultentrypoints=web,websecure
--keeptrailingslash=true --keeptrailingslash=true
``` ```
@ -1156,21 +1156,21 @@ As the dashboard access is now secured by default you can either:
## static configuration ## static configuration
# traefik.toml # traefik.toml
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[entryPoints.web-secure.tls] [entryPoints.websecure.tls]
[entryPoints.web-secure.auth] [entryPoints.websecure.auth]
[entryPoints.web-secure.auth.basic] [entryPoints.websecure.auth.basic]
users = [ users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
] ]
[api] [api]
entryPoint = "web-secure" entryPoint = "websecure"
``` ```
```bash tab="CLI" ```bash tab="CLI"
--entryPoints='Name:web-secure Address::443 TLS Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/' --entryPoints='Name:websecure Address::443 TLS Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/'
--api --api
``` ```
@ -1180,7 +1180,7 @@ As the dashboard access is now secured by default you can either:
# dynamic configuration # dynamic configuration
labels: labels:
- "traefik.http.routers.api.rule=Host(`traefik.docker.localhost`)" - "traefik.http.routers.api.rule=Host(`traefik.docker.localhost`)"
- "traefik.http.routers.api.entrypoints=web-secured" - "traefik.http.routers.api.entrypoints=websecured"
- "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.service=api@internal"
- "traefik.http.routers.api.middlewares=myAuth" - "traefik.http.routers.api.middlewares=myAuth"
- "traefik.http.routers.api.tls" - "traefik.http.routers.api.tls"
@ -1191,7 +1191,7 @@ As the dashboard access is now secured by default you can either:
## static configuration ## static configuration
# traefik.toml # traefik.toml
[entryPoints.web-secure] [entryPoints.websecure]
address = ":443" address = ":443"
[api] [api]
@ -1206,7 +1206,7 @@ As the dashboard access is now secured by default you can either:
[http.routers.api] [http.routers.api]
rule = "Host(`traefik.docker.localhost`)" rule = "Host(`traefik.docker.localhost`)"
entrypoints = ["web-secure"] entrypoints = ["websecure"]
service = "api@internal" service = "api@internal"
middlewares = ["myAuth"] middlewares = ["myAuth"]
[http.routers.api.tls] [http.routers.api.tls]
@ -1222,7 +1222,7 @@ As the dashboard access is now secured by default you can either:
# traefik.yaml # traefik.yaml
entryPoints: entryPoints:
web-secure: websecure:
address: ':443' address: ':443'
api: {} api: {}
@ -1241,7 +1241,7 @@ As the dashboard access is now secured by default you can either:
api: api:
rule: Host(`traefik.docker.localhost`) rule: Host(`traefik.docker.localhost`)
entrypoints: entrypoints:
- web-secure - websecure
service: api@internal service: api@internal
middlewares: middlewares:
- myAuth - myAuth

View file

@ -60,7 +60,7 @@ If you require LetsEncrypt with HA in a kubernetes environment, we recommend usi
If you are wanting to continue to run Traefik Community Edition, LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html). If you are wanting to continue to run Traefik Community Edition, LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html).
When using Cert-Manager to manage certificates, it will create secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). When using Cert-Manager to manage certificates, it will create secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls).
When using the Traefik Kubernetes CRD Provider, unfortunately Cert-Manager cannot interface directly with the CRDs _yet_, but this is being worked on by our team. When using the Traefik Kubernetes CRD Provider, unfortunately Cert-Manager cannot interface directly with the CRDs _yet_, but this is being worked on by our team.
A workaround it to enable the [Kubernetes Ingress provider](./kubernetes-ingress.md) to allow Cert-Manager to create ingress objects to complete the challenges. A workaround is to enable the [Kubernetes Ingress provider](./kubernetes-ingress.md) to allow Cert-Manager to create ingress objects to complete the challenges.
Please note that this still requires manual intervention to create the certificates through Cert-Manager, but once created, Cert-Manager will keep the certificate renewed. Please note that this still requires manual intervention to create the certificates through Cert-Manager, but once created, Cert-Manager will keep the certificate renewed.
## Provider Configuration ## Provider Configuration

View file

@ -78,7 +78,7 @@ metadata:
spec: spec:
entryPoints: entryPoints:
- web - web
- web-secure - websecure
routes: routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`) - match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule kind: Rule

View file

@ -152,7 +152,7 @@ metadata:
spec: spec:
entryPoints: entryPoints:
- web - web
- web-secure - websecure
routes: routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`) - match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule kind: Rule

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}
@ -45,7 +45,7 @@
[http.routers] [http.routers]
[http.routers.test] [http.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "Host(`traefik.acme.wtf`)" rule = "Host(`traefik.acme.wtf`)"
service = "test" service = "test"
[http.routers.test.tls] [http.routers.test.tls]

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}
@ -45,7 +45,7 @@
[http.routers] [http.routers]
[http.routers.test] [http.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "PathPrefix(`/`)" rule = "PathPrefix(`/`)"
service = "test" service = "test"
[http.routers.test.tls] [http.routers.test.tls]

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}
@ -45,14 +45,14 @@
[http.routers] [http.routers]
[http.routers.test] [http.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "Host(`traefik.acme.wtf`)" rule = "Host(`traefik.acme.wtf`)"
service = "test" service = "test"
[http.routers.test.tls] [http.routers.test.tls]
certResolver = "default" certResolver = "default"
[http.routers.tchouk] [http.routers.tchouk]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "Host(`tchouk.acme.wtf`)" rule = "Host(`tchouk.acme.wtf`)"
service = "test" service = "test"
[http.routers.tchouk.tls] [http.routers.tchouk.tls]

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}
@ -45,7 +45,7 @@
[tcp.routers] [tcp.routers]
[tcp.routers.test] [tcp.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "HostSNI(`traefik.acme.wtf`)" rule = "HostSNI(`traefik.acme.wtf`)"
service = "test" service = "test"
[tcp.routers.test.tls] [tcp.routers.test.tls]

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}
@ -45,7 +45,7 @@
[http.routers] [http.routers]
[http.routers.test] [http.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "Host(`traefik.acme.wtf`)" rule = "Host(`traefik.acme.wtf`)"
service = "test" service = "test"
[http.routers.test.tls] [http.routers.test.tls]

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
{{range $name, $resolvers := .Acme }} {{range $name, $resolvers := .Acme }}

View file

@ -8,7 +8,7 @@
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = "{{ .PortHTTP }}" address = "{{ .PortHTTP }}"
[entryPoints.web-secure] [entryPoints.websecure]
address = "{{ .PortHTTPS }}" address = "{{ .PortHTTPS }}"
[entryPoints.traefik] [entryPoints.traefik]

View file

@ -5,7 +5,7 @@
[http.routers] [http.routers]
[http.routers.test] [http.routers.test]
entryPoints = ["web-secure"] entryPoints = ["websecure"]
rule = "Host(`traefik.acme.wtf`)" rule = "Host(`traefik.acme.wtf`)"
service = "test" service = "test"
[http.routers.test.tls] [http.routers.test.tls]

View file

@ -9,7 +9,7 @@
rootCAs = [ """{{ .CertContent }}""" ] rootCAs = [ """{{ .CertContent }}""" ]
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -9,7 +9,7 @@
insecureSkipVerify = true insecureSkipVerify = true
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -9,7 +9,7 @@
rootCAs = [ """{{ .CertContent }}""" ] rootCAs = [ """{{ .CertContent }}""" ]
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[entryPoints.https02] [entryPoints.https02]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -9,7 +9,7 @@
[entryPoints.web] [entryPoints.web]
address = ":8888" address = ":8888"
[entryPoints.web-secure] [entryPoints.websecure]
address = ":8443" address = ":8443"
[api] [api]
@ -28,7 +28,7 @@
service = "service1" service = "service1"
[http.routers.router1TLS] [http.routers.router1TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`example.com`)" rule = "Host(`example.com`)"
service = "service1" service = "service1"
[http.routers.router1TLS.tls] [http.routers.router1TLS.tls]
@ -40,7 +40,7 @@
service = "service1" service = "service1"
[http.routers.router2TLS] [http.routers.router2TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`example2.com`)" rule = "Host(`example2.com`)"
service = "service1" service = "service1"
[http.routers.router2TLS.tls] [http.routers.router2TLS.tls]
@ -52,7 +52,7 @@
service = "service1" service = "service1"
[http.routers.router3TLS] [http.routers.router3TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`test.com`)" rule = "Host(`test.com`)"
service = "service1" service = "service1"
[http.routers.router3TLS.tls] [http.routers.router3TLS.tls]
@ -64,7 +64,7 @@
service = "service1" service = "service1"
[http.routers.router4TLS] [http.routers.router4TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`test2.com`)" rule = "Host(`test2.com`)"
service = "service1" service = "service1"
[http.routers.router4TLS.tls] [http.routers.router4TLS.tls]
@ -76,7 +76,7 @@
service = "service1" service = "service1"
[http.routers.router5TLS] [http.routers.router5TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`foo.com`)" rule = "Host(`foo.com`)"
service = "service1" service = "service1"
[http.routers.router5TLS.tls] [http.routers.router5TLS.tls]
@ -88,7 +88,7 @@
service = "service1" service = "service1"
[http.routers.router6TLS] [http.routers.router6TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`foo2.com`)" rule = "Host(`foo2.com`)"
service = "service1" service = "service1"
[http.routers.router6TLS.tls] [http.routers.router6TLS.tls]
@ -100,7 +100,7 @@
service = "service1" service = "service1"
[http.routers.router7TLS] [http.routers.router7TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`bar.com`)" rule = "Host(`bar.com`)"
service = "service1" service = "service1"
[http.routers.router7TLS.tls] [http.routers.router7TLS.tls]
@ -112,7 +112,7 @@
service = "service1" service = "service1"
[http.routers.router8TLS] [http.routers.router8TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`bar2.com`)" rule = "Host(`bar2.com`)"
service = "service1" service = "service1"
[http.routers.router8TLS.tls] [http.routers.router8TLS.tls]
@ -124,7 +124,7 @@
service = "service1" service = "service1"
[http.routers.router9TLS] [http.routers.router9TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`pow.com`)" rule = "Host(`pow.com`)"
service = "service1" service = "service1"
[http.routers.router9TLS.tls] [http.routers.router9TLS.tls]
@ -136,7 +136,7 @@
service = "service1" service = "service1"
[http.routers.router10TLS] [http.routers.router10TLS]
entryPoints = [ "web-secure" ] entryPoints = [ "websecure" ]
rule = "Host(`pow2.com`)" rule = "Host(`pow2.com`)"
service = "service1" service = "service1"
[http.routers.router10TLS.tls] [http.routers.router10TLS.tls]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -6,7 +6,7 @@
level = "DEBUG" level = "DEBUG"
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":4443" address = ":4443"
[api] [api]

View file

@ -9,7 +9,7 @@
rootCAs = [ """{{ .RootCertContent }}""" ] rootCAs = [ """{{ .RootCertContent }}""" ]
[entryPoints] [entryPoints]
[entryPoints.web-secure] [entryPoints.websecure]
address = ":8443" address = ":8443"
[api] [api]

View file

@ -67,7 +67,7 @@ func TestHandler_EntryPoints(t *testing.T) {
TrustedIPs: []string{"192.168.1.3", "192.168.1.4"}, TrustedIPs: []string{"192.168.1.3", "192.168.1.4"},
}, },
}, },
"web-secure": { "websecure": {
Address: ":443", Address: ":443",
Transport: &static.EntryPointsTransport{ Transport: &static.EntryPointsTransport{
LifeCycle: &static.LifeCycle{ LifeCycle: &static.LifeCycle{

View file

@ -37,7 +37,7 @@
"192.168.1.40" "192.168.1.40"
] ]
}, },
"name": "web-secure", "name": "websecure",
"proxyProtocol": { "proxyProtocol": {
"insecure": true, "insecure": true,
"trustedIPs": [ "trustedIPs": [

View file

@ -2,12 +2,12 @@ package auth
import ( import (
"context" "context"
"crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
@ -29,7 +29,7 @@ type forwardAuth struct {
authResponseHeaders []string authResponseHeaders []string
next http.Handler next http.Handler
name string name string
tlsConfig *tls.Config client http.Client
trustForwardHeader bool trustForwardHeader bool
} }
@ -45,13 +45,23 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
trustForwardHeader: config.TrustForwardHeader, trustForwardHeader: config.TrustForwardHeader,
} }
// Ensure our request client does not follow redirects
fa.client = http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: 30 * time.Second,
}
if config.TLS != nil { if config.TLS != nil {
tlsConfig, err := config.TLS.CreateTLSConfig() tlsConfig, err := config.TLS.CreateTLSConfig()
if err != nil { if err != nil {
return nil, err return nil, err
} }
fa.tlsConfig = tlsConfig tr := http.DefaultTransport.(*http.Transport).Clone()
tr.TLSClientConfig = tlsConfig
fa.client.Transport = tr
} }
return fa, nil return fa, nil
@ -64,19 +74,6 @@ func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
logger := log.FromContext(middlewares.GetLoggerCtx(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{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
if fa.tlsConfig != nil {
httpClient.Transport = &http.Transport{
TLSClientConfig: fa.tlsConfig,
}
}
forwardReq, err := http.NewRequest(http.MethodGet, fa.address, nil) forwardReq, err := http.NewRequest(http.MethodGet, fa.address, nil)
tracing.LogRequest(tracing.GetSpan(req), forwardReq) tracing.LogRequest(tracing.GetSpan(req), forwardReq)
if err != nil { if err != nil {
@ -94,7 +91,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
writeHeader(req, forwardReq, fa.trustForwardHeader) writeHeader(req, forwardReq, fa.trustForwardHeader)
forwardResponse, forwardErr := httpClient.Do(forwardReq) forwardResponse, forwardErr := fa.client.Do(forwardReq)
if forwardErr != nil { if forwardErr != nil {
logMessage := fmt.Sprintf("Error calling %s. Cause: %s", fa.address, forwardErr) logMessage := fmt.Sprintf("Error calling %s. Cause: %s", fa.address, forwardErr)
logger.Debug(logMessage) logger.Debug(logMessage)

View file

@ -179,12 +179,12 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
p.renewCertificates(ctx) p.renewCertificates(ctx)
ticker := time.NewTicker(24 * time.Hour) ticker := time.NewTicker(24 * time.Hour)
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
p.renewCertificates(ctx) p.renewCertificates(ctx)
case <-stop: case <-ctxPool.Done():
ticker.Stop() ticker.Stop()
return return
} }
@ -341,7 +341,7 @@ func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStor
} }
func (p *Provider) watchNewDomains(ctx context.Context) { func (p *Provider) watchNewDomains(ctx context.Context) {
p.pool.Go(func(stop chan bool) { p.pool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case config := <-p.configFromListenerChan: case config := <-p.configFromListenerChan:
@ -415,7 +415,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
p.resolveDomains(ctxRouter, domains, tlsStore) p.resolveDomains(ctxRouter, domains, tlsStore)
} }
} }
case <-stop: case <-ctxPool.Done():
return return
} }
} }
@ -556,7 +556,7 @@ func deleteUnnecessaryDomains(ctx context.Context, domains []types.Domain) []typ
func (p *Provider) watchCertificate(ctx context.Context) { func (p *Provider) watchCertificate(ctx context.Context) {
p.certsChan = make(chan *CertAndStore) p.certsChan = make(chan *CertAndStore)
p.pool.Go(func(stop chan bool) { p.pool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case cert := <-p.certsChan: case cert := <-p.certsChan:
@ -576,7 +576,7 @@ func (p *Provider) watchCertificate(ctx context.Context) {
if err != nil { if err != nil {
log.FromContext(ctx).Error(err) log.FromContext(ctx).Error(err)
} }
case <-stop: case <-ctxPool.Done():
return return
} }
} }

View file

@ -103,11 +103,11 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
} }
// Process events // Process events
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctx context.Context) {
defer watcher.Close() defer watcher.Close()
for { for {
select { select {
case <-stop: case <-ctx.Done():
return return
case evt := <-watcher.Events: case evt := <-watcher.Events:
if p.Directory == "" { if p.Directory == "" {

View file

@ -66,7 +66,7 @@ metadata:
spec: spec:
ports: ports:
- name: web-secure - name: websecure
port: 443 port: 443
targetPort: 8443 targetPort: 8443
selector: selector:
@ -85,7 +85,7 @@ subsets:
- ip: 10.10.0.5 - ip: 10.10.0.5
- ip: 10.10.0.6 - ip: 10.10.0.6
ports: ports:
- name: web-secure - name: websecure
port: 8443 port: 8443
--- ---
@ -97,7 +97,7 @@ metadata:
spec: spec:
ports: ports:
- name: web-secure2 - name: websecure2
port: 8443 port: 8443
scheme: https scheme: https
selector: selector:
@ -116,5 +116,5 @@ subsets:
- ip: 10.10.0.7 - ip: 10.10.0.7
- ip: 10.10.0.8 - ip: 10.10.0.8
ports: ports:
- name: web-secure2 - name: websecure2
port: 8443 port: 8443

View file

@ -66,7 +66,7 @@ metadata:
spec: spec:
ports: ports:
- name: web-secure - name: websecure
port: 443 port: 443
selector: selector:
app: containous app: containous
@ -84,7 +84,7 @@ subsets:
- ip: 10.10.0.5 - ip: 10.10.0.5
- ip: 10.10.0.6 - ip: 10.10.0.6
ports: ports:
- name: web-secure - name: websecure
port: 443 port: 443
--- ---

View file

@ -98,11 +98,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return err return err
} }
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctxPool context.Context) {
operation := func() error { operation := func() error {
stopWatch := make(chan struct{}, 1) eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
defer close(stopWatch)
eventsChan, err := k8sClient.WatchAll(p.Namespaces, stopWatch)
if err != nil { if err != nil {
logger.Errorf("Error watching kubernetes events: %v", err) logger.Errorf("Error watching kubernetes events: %v", err)
@ -110,20 +108,20 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
select { select {
case <-timer.C: case <-timer.C:
return err return err
case <-stop: case <-ctxPool.Done():
return nil return nil
} }
} }
throttleDuration := time.Duration(p.ThrottleDuration) throttleDuration := time.Duration(p.ThrottleDuration)
throttledChan := throttleEvents(ctxLog, throttleDuration, stop, eventsChan) throttledChan := throttleEvents(ctxLog, throttleDuration, pool, eventsChan)
if throttledChan != nil { if throttledChan != nil {
eventsChan = throttledChan eventsChan = throttledChan
} }
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return nil return nil
case event := <-eventsChan: case event := <-eventsChan:
// Note that event is the *first* event that came in during this throttling interval -- if we're hitting our throttle, we may have dropped events. // Note that event is the *first* event that came in during this throttling interval -- if we're hitting our throttle, we may have dropped events.
@ -156,7 +154,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
notify := func(err error, time time.Duration) { notify := func(err error, time time.Duration) {
logger.Errorf("Provider connection error: %v; retrying in %s", err, time) logger.Errorf("Provider connection error: %v; retrying in %s", err, time)
} }
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify) err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
if err != nil { if err != nil {
logger.Errorf("Cannot connect to Provider: %v", err) logger.Errorf("Cannot connect to Provider: %v", err)
} }
@ -625,7 +623,7 @@ func getCABlocks(secret *corev1.Secret, namespace, secretName string) (string, e
return cert, nil return cert, nil
} }
func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop chan bool, eventsChan <-chan interface{}) chan interface{} { func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
if throttleDuration == 0 { if throttleDuration == 0 {
return nil return nil
} }
@ -635,10 +633,10 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop ch
// Run a goroutine that reads events from eventChan and does a non-blocking write to pendingEvent. // Run a goroutine that reads events from eventChan and does a non-blocking write to pendingEvent.
// This guarantees that writing to eventChan will never block, // This guarantees that writing to eventChan will never block,
// and that pendingEvent will have something in it if there's been an event since we read from that channel. // and that pendingEvent will have something in it if there's been an event since we read from that channel.
go func() { pool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return return
case nextEvent := <-eventsChan: case nextEvent := <-eventsChan:
select { select {
@ -650,7 +648,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop ch
} }
} }
} }
}() })
return eventsChanBuffered return eventsChanBuffered
} }

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,15 @@
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: ""
namespace: testing
spec:
rules:
- host: "*.foobar.com"
http:
paths:
- path: /bar
backend:
serviceName: service1
servicePort: 80

View file

@ -0,0 +1,11 @@
---
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIp: 10.0.0.1

View file

@ -105,32 +105,29 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return err return err
} }
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctxPool context.Context) {
operation := func() error { operation := func() error {
stopWatch := make(chan struct{}, 1) eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
defer close(stopWatch)
eventsChan, err := k8sClient.WatchAll(p.Namespaces, stopWatch)
if err != nil { if err != nil {
logger.Errorf("Error watching kubernetes events: %v", err) logger.Errorf("Error watching kubernetes events: %v", err)
timer := time.NewTimer(1 * time.Second) timer := time.NewTimer(1 * time.Second)
select { select {
case <-timer.C: case <-timer.C:
return err return err
case <-stop: case <-ctxPool.Done():
return nil return nil
} }
} }
throttleDuration := time.Duration(p.ThrottleDuration) throttleDuration := time.Duration(p.ThrottleDuration)
throttledChan := throttleEvents(ctxLog, throttleDuration, stop, eventsChan) throttledChan := throttleEvents(ctxLog, throttleDuration, pool, eventsChan)
if throttledChan != nil { if throttledChan != nil {
eventsChan = throttledChan eventsChan = throttledChan
} }
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return nil return nil
case event := <-eventsChan: case event := <-eventsChan:
// Note that event is the *first* event that came in during this // Note that event is the *first* event that came in during this
@ -165,7 +162,8 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
notify := func(err error, time time.Duration) { notify := func(err error, time time.Duration) {
logger.Errorf("Provider connection error: %s; retrying in %s", err, time) logger.Errorf("Provider connection error: %s; retrying in %s", err, time)
} }
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
if err != nil { if err != nil {
logger.Errorf("Cannot connect to Provider: %s", err) logger.Errorf("Cannot connect to Provider: %s", err)
} }
@ -267,6 +265,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.ServiceName + "-" + pa.Backend.ServicePort.String()) serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.ServiceName + "-" + pa.Backend.ServicePort.String())
conf.HTTP.Services[serviceName] = service conf.HTTP.Services[serviceName] = service
conf.HTTP.Services[serviceName] = service
routerKey := strings.TrimPrefix(provider.Normalize(rule.Host+pa.Path), "-") routerKey := strings.TrimPrefix(provider.Normalize(rule.Host+pa.Path), "-")
conf.HTTP.Routers[routerKey] = loadRouter(ingress, rule, pa, rtConfig, serviceName) conf.HTTP.Routers[routerKey] = loadRouter(ingress, rule, pa, rtConfig, serviceName)
@ -323,6 +322,14 @@ func (p *Provider) updateIngressStatus(ing *v1beta1.Ingress, k8sClient Client) e
return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress[0].IP, service.Status.LoadBalancer.Ingress[0].Hostname) return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress[0].IP, service.Status.LoadBalancer.Ingress[0].Hostname)
} }
func buildHostRule(host string) string {
if strings.HasPrefix(host, "*.") {
return "HostRegexp(`" + strings.Replace(host, "*.", "{subdomain:[a-zA-Z0-9-]+}.", 1) + "`)"
}
return "Host(`" + host + "`)"
}
func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool { func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool {
return ingressClass == ingressClassAnnotation || return ingressClass == ingressClassAnnotation ||
(len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass) (len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass)
@ -522,7 +529,7 @@ func getProtocol(portSpec corev1.ServicePort, portName string, svcConfig *Servic
func loadRouter(ingress *v1beta1.Ingress, rule v1beta1.IngressRule, pa v1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router { func loadRouter(ingress *v1beta1.Ingress, rule v1beta1.IngressRule, pa v1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
var rules []string var rules []string
if len(rule.Host) > 0 { if len(rule.Host) > 0 {
rules = []string{"Host(`" + rule.Host + "`)"} rules = []string{buildHostRule(rule.Host)}
} }
if len(pa.Path) > 0 { if len(pa.Path) > 0 {
@ -562,7 +569,7 @@ func checkStringQuoteValidity(value string) error {
return err return err
} }
func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop chan bool, eventsChan <-chan interface{}) chan interface{} { func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
if throttleDuration == 0 { if throttleDuration == 0 {
return nil return nil
} }
@ -573,10 +580,10 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop ch
// non-blocking write to pendingEvent. This guarantees that writing to // non-blocking write to pendingEvent. This guarantees that writing to
// eventChan will never block, and that pendingEvent will have // eventChan will never block, and that pendingEvent will have
// something in it if there's been an event since we read from that channel. // something in it if there's been an event since we read from that channel.
go func() { pool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return return
case nextEvent := <-eventsChan: case nextEvent := <-eventsChan:
select { select {
@ -590,7 +597,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, stop ch
} }
} }
} }
}() })
return eventsChanBuffered return eventsChanBuffered
} }

View file

@ -980,6 +980,35 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
}, },
}, },
{
desc: "Ingress with wildcard host",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"foobar-com-bar": {
Rule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.foobar.com`) && PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
Scheme: "",
Port: "",
},
},
},
},
},
},
},
},
} }
for _, test := range testCases { for _, test := range testCases {

View file

@ -61,7 +61,6 @@ func (p *Provider) Init(storeType store.Backend, name string) error {
// Provide allows the docker provider to provide configurations to traefik using the given configuration channel. // Provide allows the docker provider to provide configurations to traefik using the given configuration channel.
func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
ctx := log.With(context.Background(), log.Str(log.ProviderName, p.name)) ctx := log.With(context.Background(), log.Str(log.ProviderName, p.name))
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
operation := func() error { operation := func() error {
@ -89,8 +88,10 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
} }
} }
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctxPool context.Context) {
err := p.watchKv(ctx, configurationChan, p.RootKey, stop) ctxLog := log.With(ctxPool, log.Str(log.ProviderName, p.name))
err := p.watchKv(ctxLog, configurationChan)
if err != nil { if err != nil {
logger.Errorf("Cannot watch KV store: %v", err) logger.Errorf("Cannot watch KV store: %v", err)
} }
@ -99,16 +100,16 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil return nil
} }
func (p *Provider) watchKv(ctx context.Context, configurationChan chan<- dynamic.Message, prefix string, stop chan bool) error { func (p *Provider) watchKv(ctx context.Context, configurationChan chan<- dynamic.Message) error {
operation := func() error { operation := func() error {
events, err := p.kvClient.WatchTree(p.RootKey, make(chan struct{}), nil) events, err := p.kvClient.WatchTree(p.RootKey, ctx.Done(), nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to watch KV: %w", err) return fmt.Errorf("failed to watch KV: %w", err)
} }
for { for {
select { select {
case <-stop: case <-ctx.Done():
return nil return nil
case _, ok := <-events: case _, ok := <-events:
if !ok { if !ok {
@ -133,7 +134,9 @@ func (p *Provider) watchKv(ctx context.Context, configurationChan chan<- dynamic
notify := func(err error, time time.Duration) { notify := func(err error, time time.Duration) {
log.FromContext(ctx).Errorf("KV connection error: %+v, retrying in %s", err, time) log.FromContext(ctx).Errorf("KV connection error: %+v, retrying in %s", err, time)
} }
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
err := backoff.RetryNotify(safe.OperationWithRecover(operation),
backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctx), notify)
if err != nil { if err != nil {
return fmt.Errorf("cannot connect to KV server: %w", err) return fmt.Errorf("cannot connect to KV server: %w", err)
} }

View file

@ -857,7 +857,7 @@ func TestKvWatchTree(t *testing.T) {
configChan := make(chan dynamic.Message) configChan := make(chan dynamic.Message)
go func() { go func() {
err := provider.watchKv(context.Background(), configChan, "prefix", make(chan bool, 1)) err := provider.watchKv(context.Background(), configChan)
require.NoError(t, err) require.NoError(t, err)
}() }()

View file

@ -159,11 +159,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
logger.Errorf("Failed to register for events, %s", err) logger.Errorf("Failed to register for events, %s", err)
return err return err
} }
pool.Go(func(stop chan bool) { pool.GoCtx(func(ctxPool context.Context) {
defer close(update) defer close(update)
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return return
case event := <-update: case event := <-update:
logger.Debugf("Received provider event %s", event) logger.Debugf("Received provider event %s", event)

View file

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/server/provider"
) )
// NewBuilder creates a builder. // NewBuilder creates a builder.
@ -22,21 +23,28 @@ func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response
var modifiers []func(*http.Response) error var modifiers []func(*http.Response) error
for _, middleName := range names { for _, middleName := range names {
if conf, ok := f.configs[middleName]; ok { conf, ok := f.configs[middleName]
if conf == nil || conf.Middleware == nil { if !ok {
getLogger(ctx, middleName, "undefined").Error("Invalid Middleware configuration (ResponseModifier)") getLogger(ctx, middleName, "undefined").Debug("Middleware name not found in config (ResponseModifier)")
continue continue
} }
if conf == nil || conf.Middleware == nil {
if conf.Headers != nil { getLogger(ctx, middleName, "undefined").Error("Invalid Middleware configuration (ResponseModifier)")
getLogger(ctx, middleName, "Headers").Debug("Creating Middleware (ResponseModifier)") continue
}
modifiers = append(modifiers, buildHeaders(conf.Headers))
} else if conf.Chain != nil { if conf.Headers != nil {
getLogger(ctx, middleName, "Chain").Debug("Creating Middleware (ResponseModifier)") getLogger(ctx, middleName, "Headers").Debug("Creating Middleware (ResponseModifier)")
modifiers = append(modifiers, f.Build(ctx, conf.Chain.Middlewares)) modifiers = append(modifiers, buildHeaders(conf.Headers))
} else if conf.Chain != nil {
chainCtx := provider.AddInContext(ctx, middleName)
getLogger(chainCtx, middleName, "Chain").Debug("Creating Middleware (ResponseModifier)")
var qualifiedNames []string
for _, name := range conf.Chain.Middlewares {
qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(chainCtx, name))
} }
modifiers = append(modifiers, f.Build(ctx, qualifiedNames))
} }
} }

View file

@ -10,88 +10,37 @@ import (
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
) )
type routine struct {
goroutine func(chan bool)
stop chan bool
}
type routineCtx func(ctx context.Context) type routineCtx func(ctx context.Context)
// Pool is a pool of go routines // Pool is a pool of go routines
type Pool struct { type Pool struct {
routines []routine waitGroup sync.WaitGroup
waitGroup sync.WaitGroup ctx context.Context
lock sync.Mutex cancel context.CancelFunc
baseCtx context.Context
baseCancel context.CancelFunc
ctx context.Context
cancel context.CancelFunc
} }
// NewPool creates a Pool // NewPool creates a Pool
func NewPool(parentCtx context.Context) *Pool { func NewPool(parentCtx context.Context) *Pool {
baseCtx, baseCancel := context.WithCancel(parentCtx) ctx, cancel := context.WithCancel(parentCtx)
ctx, cancel := context.WithCancel(baseCtx)
return &Pool{ return &Pool{
baseCtx: baseCtx, ctx: ctx,
baseCancel: baseCancel, cancel: cancel,
ctx: ctx,
cancel: cancel,
} }
} }
// Ctx returns main context
func (p *Pool) Ctx() context.Context {
return p.baseCtx
}
// GoCtx starts a recoverable goroutine with a context // GoCtx starts a recoverable goroutine with a context
func (p *Pool) GoCtx(goroutine routineCtx) { func (p *Pool) GoCtx(goroutine routineCtx) {
p.lock.Lock()
p.waitGroup.Add(1) p.waitGroup.Add(1)
Go(func() { Go(func() {
defer p.waitGroup.Done() defer p.waitGroup.Done()
goroutine(p.ctx) goroutine(p.ctx)
}) })
p.lock.Unlock()
}
// Go starts a recoverable goroutine, and can be stopped with stop chan
func (p *Pool) Go(goroutine func(stop chan bool)) {
p.lock.Lock()
newRoutine := routine{
goroutine: goroutine,
stop: make(chan bool, 1),
}
p.routines = append(p.routines, newRoutine)
p.waitGroup.Add(1)
Go(func() {
defer p.waitGroup.Done()
goroutine(newRoutine.stop)
})
p.lock.Unlock()
} }
// Stop stops all started routines, waiting for their termination // Stop stops all started routines, waiting for their termination
func (p *Pool) Stop() { func (p *Pool) Stop() {
p.lock.Lock()
defer p.lock.Unlock()
p.cancel() p.cancel()
for _, routine := range p.routines {
routine.stop <- true
}
p.waitGroup.Wait() p.waitGroup.Wait()
for _, routine := range p.routines {
close(routine.stop)
}
}
// Cleanup releases resources used by the pool, and should be called when the pool will no longer be used
func (p *Pool) Cleanup() {
p.Stop()
p.lock.Lock()
defer p.lock.Unlock()
p.baseCancel()
} }
// Go starts a recoverable goroutine // Go starts a recoverable goroutine

View file

@ -18,12 +18,13 @@ func TestNewPoolContext(t *testing.T) {
ctx := context.WithValue(context.Background(), testKey, "test") ctx := context.WithValue(context.Background(), testKey, "test")
p := NewPool(ctx) p := NewPool(ctx)
retCtx := p.Ctx() p.GoCtx(func(ctx context.Context) {
retCtxVal, ok := ctx.Value(testKey).(string)
retCtxVal, ok := retCtx.Value(testKey).(string) if !ok || retCtxVal != "test" {
if !ok || retCtxVal != "test" { t.Errorf("Pool.Ctx() did not return a derived context, got %#v, expected context with test value", ctx)
t.Errorf("Pool.Ctx() did not return a derived context, got %#v, expected context with test value", retCtx) }
} })
p.Stop()
} }
type fakeRoutine struct { type fakeRoutine struct {
@ -46,14 +47,6 @@ func (tr *fakeRoutine) routineCtx(ctx context.Context) {
<-ctx.Done() <-ctx.Done()
} }
func (tr *fakeRoutine) routine(stop chan bool) {
tr.Lock()
tr.started = true
tr.Unlock()
tr.startSig <- true
<-stop
}
func TestPoolWithCtx(t *testing.T) { func TestPoolWithCtx(t *testing.T) {
testRoutine := newFakeRoutine() testRoutine := newFakeRoutine()
@ -79,12 +72,12 @@ func TestPoolWithCtx(t *testing.T) {
defer timer.Stop() defer timer.Stop()
test.fn(p) test.fn(p)
defer p.Cleanup() defer p.Stop()
testDone := make(chan bool, 1) testDone := make(chan bool, 1)
go func() { go func() {
<-testRoutine.startSig <-testRoutine.startSig
p.Cleanup() p.Stop()
testDone <- true testDone <- true
}() }()
@ -100,89 +93,30 @@ func TestPoolWithCtx(t *testing.T) {
} }
} }
func TestPoolWithStopChan(t *testing.T) { func TestPoolCleanupWithGoPanicking(t *testing.T) {
testRoutine := newFakeRoutine()
p := NewPool(context.Background()) p := NewPool(context.Background())
timer := time.NewTimer(500 * time.Millisecond) timer := time.NewTimer(500 * time.Millisecond)
defer timer.Stop() defer timer.Stop()
p.Go(testRoutine.routine) p.GoCtx(func(ctx context.Context) {
if len(p.routines) != 1 { panic("BOOM")
t.Fatalf("After Pool.Go(func), Pool did have %d goroutines, expected 1", len(p.routines)) })
}
testDone := make(chan bool, 1) testDone := make(chan bool, 1)
go func() { go func() {
<-testRoutine.startSig p.Stop()
p.Cleanup()
testDone <- true testDone <- true
}() }()
select { select {
case <-timer.C: case <-timer.C:
testRoutine.Lock() t.Fatalf("Pool.Cleanup() did not complete in time with a panicking goroutine")
defer testRoutine.Unlock()
t.Fatalf("Pool test did not complete in time, goroutine started equals '%t'", testRoutine.started)
case <-testDone: case <-testDone:
return return
} }
} }
func TestPoolCleanupWithGoPanicking(t *testing.T) {
testRoutine := func(stop chan bool) {
panic("BOOM")
}
testCtxRoutine := func(ctx context.Context) {
panic("BOOM")
}
testCases := []struct {
desc string
fn func(*Pool)
}{
{
desc: "Go()",
fn: func(p *Pool) {
p.Go(testRoutine)
},
},
{
desc: "GoCtx()",
fn: func(p *Pool) {
p.GoCtx(testCtxRoutine)
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
p := NewPool(context.Background())
timer := time.NewTimer(500 * time.Millisecond)
defer timer.Stop()
test.fn(p)
testDone := make(chan bool, 1)
go func() {
p.Cleanup()
testDone <- true
}()
select {
case <-timer.C:
t.Fatalf("Pool.Cleanup() did not complete in time with a panicking goroutine")
case <-testDone:
return
}
})
}
}
func TestGoroutineRecover(t *testing.T) { func TestGoroutineRecover(t *testing.T) {
// if recover fails the test will panic // if recover fails the test will panic
Go(func() { Go(func() {

View file

@ -3,7 +3,7 @@ package server
import ( import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/containous/traefik/v2/pkg/tls" "github.com/containous/traefik/v2/pkg/tls"
) )
@ -25,25 +25,25 @@ func mergeConfiguration(configurations dynamic.Configurations) dynamic.Configura
} }
var defaultTLSOptionProviders []string var defaultTLSOptionProviders []string
for provider, configuration := range configurations { for pvd, configuration := range configurations {
if configuration.HTTP != nil { if configuration.HTTP != nil {
for routerName, router := range configuration.HTTP.Routers { for routerName, router := range configuration.HTTP.Routers {
conf.HTTP.Routers[internal.MakeQualifiedName(provider, routerName)] = router conf.HTTP.Routers[provider.MakeQualifiedName(pvd, routerName)] = router
} }
for middlewareName, middleware := range configuration.HTTP.Middlewares { for middlewareName, middleware := range configuration.HTTP.Middlewares {
conf.HTTP.Middlewares[internal.MakeQualifiedName(provider, middlewareName)] = middleware conf.HTTP.Middlewares[provider.MakeQualifiedName(pvd, middlewareName)] = middleware
} }
for serviceName, service := range configuration.HTTP.Services { for serviceName, service := range configuration.HTTP.Services {
conf.HTTP.Services[internal.MakeQualifiedName(provider, serviceName)] = service conf.HTTP.Services[provider.MakeQualifiedName(pvd, serviceName)] = service
} }
} }
if configuration.TCP != nil { if configuration.TCP != nil {
for routerName, router := range configuration.TCP.Routers { for routerName, router := range configuration.TCP.Routers {
conf.TCP.Routers[internal.MakeQualifiedName(provider, routerName)] = router conf.TCP.Routers[provider.MakeQualifiedName(pvd, routerName)] = router
} }
for serviceName, service := range configuration.TCP.Services { for serviceName, service := range configuration.TCP.Services {
conf.TCP.Services[internal.MakeQualifiedName(provider, serviceName)] = service conf.TCP.Services[provider.MakeQualifiedName(pvd, serviceName)] = service
} }
} }
@ -56,9 +56,9 @@ func mergeConfiguration(configurations dynamic.Configurations) dynamic.Configura
for tlsOptionsName, options := range configuration.TLS.Options { for tlsOptionsName, options := range configuration.TLS.Options {
if tlsOptionsName != "default" { if tlsOptionsName != "default" {
tlsOptionsName = internal.MakeQualifiedName(provider, tlsOptionsName) tlsOptionsName = provider.MakeQualifiedName(pvd, tlsOptionsName)
} else { } else {
defaultTLSOptionProviders = append(defaultTLSOptionProviders, provider) defaultTLSOptionProviders = append(defaultTLSOptionProviders, pvd)
} }
conf.TLS.Options[tlsOptionsName] = options conf.TLS.Options[tlsOptionsName] = options

View file

@ -1,6 +1,7 @@
package server package server
import ( import (
"context"
"encoding/json" "encoding/json"
"reflect" "reflect"
"time" "time"
@ -49,8 +50,8 @@ func NewConfigurationWatcher(routinesPool *safe.Pool, pvd provider.Provider, pro
// Start the configuration watcher. // Start the configuration watcher.
func (c *ConfigurationWatcher) Start() { func (c *ConfigurationWatcher) Start() {
c.routinesPool.Go(c.listenProviders) c.routinesPool.GoCtx(c.listenProviders)
c.routinesPool.Go(c.listenConfigurations) c.routinesPool.GoCtx(c.listenConfigurations)
c.startProvider() c.startProvider()
} }
@ -90,10 +91,10 @@ func (c *ConfigurationWatcher) startProvider() {
// listenProviders receives configuration changes from the providers. // listenProviders receives configuration changes from the providers.
// The configuration message then gets passed along a series of check // The configuration message then gets passed along a series of check
// to finally end up in a throttler that sends it to listenConfigurations (through c. configurationValidatedChan). // to finally end up in a throttler that sends it to listenConfigurations (through c. configurationValidatedChan).
func (c *ConfigurationWatcher) listenProviders(stop chan bool) { func (c *ConfigurationWatcher) listenProviders(ctx context.Context) {
for { for {
select { select {
case <-stop: case <-ctx.Done():
return return
case configMsg, ok := <-c.configurationChan: case configMsg, ok := <-c.configurationChan:
if !ok { if !ok {
@ -111,10 +112,10 @@ func (c *ConfigurationWatcher) listenProviders(stop chan bool) {
} }
} }
func (c *ConfigurationWatcher) listenConfigurations(stop chan bool) { func (c *ConfigurationWatcher) listenConfigurations(ctx context.Context) {
for { for {
select { select {
case <-stop: case <-ctx.Done():
return return
case configMsg, ok := <-c.configurationValidatedChan: case configMsg, ok := <-c.configurationValidatedChan:
if !ok || configMsg.Configuration == nil { if !ok || configMsg.Configuration == nil {
@ -150,8 +151,10 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) {
if copyConf.TLS != nil { if copyConf.TLS != nil {
copyConf.TLS.Certificates = nil copyConf.TLS.Certificates = nil
for _, v := range copyConf.TLS.Stores { for k := range copyConf.TLS.Stores {
v.DefaultCertificate = nil st := copyConf.TLS.Stores[k]
st.DefaultCertificate = nil
copyConf.TLS.Stores[k] = st
} }
} }
@ -178,8 +181,8 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) {
if !ok { if !ok {
providerConfigUpdateCh = make(chan dynamic.Message) providerConfigUpdateCh = make(chan dynamic.Message)
c.providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdateCh c.providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdateCh
c.routinesPool.Go(func(stop chan bool) { c.routinesPool.GoCtx(func(ctxPool context.Context) {
c.throttleProviderConfigReload(c.providersThrottleDuration, c.configurationValidatedChan, providerConfigUpdateCh, stop) c.throttleProviderConfigReload(ctxPool, c.providersThrottleDuration, c.configurationValidatedChan, providerConfigUpdateCh)
}) })
} }
@ -190,14 +193,14 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) {
// It will immediately publish a new configuration and then only publish the next configuration after the throttle duration. // It will immediately publish a new configuration and then only publish the next configuration after the throttle duration.
// Note that in the case it receives N new configs in the timeframe of the throttle duration after publishing, // Note that in the case it receives N new configs in the timeframe of the throttle duration after publishing,
// it will publish the last of the newly received configurations. // it will publish the last of the newly received configurations.
func (c *ConfigurationWatcher) throttleProviderConfigReload(throttle time.Duration, publish chan<- dynamic.Message, in <-chan dynamic.Message, stop chan bool) { func (c *ConfigurationWatcher) throttleProviderConfigReload(ctx context.Context, throttle time.Duration, publish chan<- dynamic.Message, in <-chan dynamic.Message) {
ring := channels.NewRingChannel(1) ring := channels.NewRingChannel(1)
defer ring.Close() defer ring.Close()
c.routinesPool.Go(func(stop chan bool) { c.routinesPool.GoCtx(func(ctxPool context.Context) {
for { for {
select { select {
case <-stop: case <-ctxPool.Done():
return return
case nextConfig := <-ring.Out(): case nextConfig := <-ring.Out():
if config, ok := nextConfig.(dynamic.Message); ok { if config, ok := nextConfig.(dynamic.Message); ok {
@ -210,7 +213,7 @@ func (c *ConfigurationWatcher) throttleProviderConfigReload(throttle time.Durati
for { for {
select { select {
case <-stop: case <-ctx.Done():
return return
case nextConfig := <-in: case nextConfig := <-in:
ring.In() <- nextConfig ring.In() <- nextConfig

View file

@ -28,7 +28,7 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/stripprefix" "github.com/containous/traefik/v2/pkg/middlewares/stripprefix"
"github.com/containous/traefik/v2/pkg/middlewares/stripprefixregex" "github.com/containous/traefik/v2/pkg/middlewares/stripprefixregex"
"github.com/containous/traefik/v2/pkg/middlewares/tracing" "github.com/containous/traefik/v2/pkg/middlewares/tracing"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
) )
type middlewareStackType int type middlewareStackType int
@ -56,10 +56,10 @@ func NewBuilder(configs map[string]*runtime.MiddlewareInfo, serviceBuilder servi
func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.Chain { func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.Chain {
chain := alice.New() chain := alice.New()
for _, name := range middlewares { for _, name := range middlewares {
middlewareName := internal.GetQualifiedName(ctx, name) middlewareName := provider.GetQualifiedName(ctx, name)
chain = chain.Append(func(next http.Handler) (http.Handler, error) { chain = chain.Append(func(next http.Handler) (http.Handler, error) {
constructorContext := internal.AddProviderInContext(ctx, middlewareName) constructorContext := provider.AddInContext(ctx, middlewareName)
if midInf, ok := b.configs[middlewareName]; !ok || midInf.Middleware == nil { if midInf, ok := b.configs[middlewareName]; !ok || midInf.Middleware == nil {
return nil, fmt.Errorf("middleware %q does not exist", middlewareName) return nil, fmt.Errorf("middleware %q does not exist", middlewareName)
} }
@ -144,7 +144,7 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (
var qualifiedNames []string var qualifiedNames []string
for _, name := range config.Chain.Middlewares { for _, name := range config.Chain.Middlewares {
qualifiedNames = append(qualifiedNames, internal.GetQualifiedName(ctx, name)) qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(ctx, name))
} }
config.Chain.Middlewares = qualifiedNames config.Chain.Middlewares = qualifiedNames
middleware = func(next http.Handler) (http.Handler, error) { middleware = func(next http.Handler) (http.Handler, error) {

View file

@ -9,7 +9,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -262,7 +262,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
ctx := context.Background() ctx := context.Background()
if len(test.contextProvider) > 0 { if len(test.contextProvider) > 0 {
ctx = internal.AddProviderInContext(ctx, "foobar@"+test.contextProvider) ctx = provider.AddInContext(ctx, "foobar@"+test.contextProvider)
} }
rtConf := runtime.NewConfig(dynamic.Configuration{ rtConf := runtime.NewConfig(dynamic.Configuration{

View file

@ -1,4 +1,4 @@
package internal package provider
import ( import (
"context" "context"
@ -10,29 +10,29 @@ import (
type contextKey int type contextKey int
const ( const (
providerKey contextKey = iota key contextKey = iota
) )
// AddProviderInContext Adds the provider name in the context // AddInContext Adds the provider name in the context
func AddProviderInContext(ctx context.Context, elementName string) context.Context { func AddInContext(ctx context.Context, elementName string) context.Context {
parts := strings.Split(elementName, "@") parts := strings.Split(elementName, "@")
if len(parts) == 1 { if len(parts) == 1 {
log.FromContext(ctx).Debugf("Could not find a provider for %s.", elementName) log.FromContext(ctx).Debugf("Could not find a provider for %s.", elementName)
return ctx return ctx
} }
if name, ok := ctx.Value(providerKey).(string); ok && name == parts[1] { if name, ok := ctx.Value(key).(string); ok && name == parts[1] {
return ctx return ctx
} }
return context.WithValue(ctx, providerKey, parts[1]) return context.WithValue(ctx, key, parts[1])
} }
// GetQualifiedName Gets the fully qualified name. // GetQualifiedName Gets the fully qualified name.
func GetQualifiedName(ctx context.Context, elementName string) string { func GetQualifiedName(ctx context.Context, elementName string) string {
parts := strings.Split(elementName, "@") parts := strings.Split(elementName, "@")
if len(parts) == 1 { if len(parts) == 1 {
if providerName, ok := ctx.Value(providerKey).(string); ok { if providerName, ok := ctx.Value(key).(string); ok {
return MakeQualifiedName(providerName, parts[0]) return MakeQualifiedName(providerName, parts[0])
} }
} }

View file

@ -1,4 +1,4 @@
package internal package provider
import ( import (
"context" "context"
@ -28,19 +28,19 @@ func TestAddProviderInContext(t *testing.T) {
}, },
{ {
desc: "provider name in context", desc: "provider name in context",
ctx: context.WithValue(context.Background(), providerKey, "foo"), ctx: context.WithValue(context.Background(), key, "foo"),
name: "test", name: "test",
expected: "foo", expected: "foo",
}, },
{ {
desc: "provider name in context and different provider name embedded in element name", desc: "provider name in context and different provider name embedded in element name",
ctx: context.WithValue(context.Background(), providerKey, "foo"), ctx: context.WithValue(context.Background(), key, "foo"),
name: "test@fii", name: "test@fii",
expected: "fii", expected: "fii",
}, },
{ {
desc: "provider name in context and same provider name embedded in element name", desc: "provider name in context and same provider name embedded in element name",
ctx: context.WithValue(context.Background(), providerKey, "foo"), ctx: context.WithValue(context.Background(), key, "foo"),
name: "test@foo", name: "test@foo",
expected: "foo", expected: "foo",
}, },
@ -51,10 +51,10 @@ func TestAddProviderInContext(t *testing.T) {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
newCtx := AddProviderInContext(test.ctx, test.name) newCtx := AddInContext(test.ctx, test.name)
var providerName string var providerName string
if name, ok := newCtx.Value(providerKey).(string); ok { if name, ok := newCtx.Value(key).(string); ok {
providerName = name providerName = name
} }
@ -90,13 +90,13 @@ func TestGetQualifiedName(t *testing.T) {
}, },
{ {
desc: "with provider in context", desc: "with provider in context",
ctx: context.WithValue(context.Background(), providerKey, "foo"), ctx: context.WithValue(context.Background(), key, "foo"),
name: "test", name: "test",
expected: "test@foo", expected: "test@foo",
}, },
{ {
desc: "with provider in context and explicit name", desc: "with provider in context and explicit name",
ctx: context.WithValue(context.Background(), providerKey, "foo"), ctx: context.WithValue(context.Background(), key, "foo"),
name: "test@fii", name: "test@fii",
expected: "test@fii", expected: "test@fii",
}, },

View file

@ -12,8 +12,8 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/recovery" "github.com/containous/traefik/v2/pkg/middlewares/recovery"
"github.com/containous/traefik/v2/pkg/middlewares/tracing" "github.com/containous/traefik/v2/pkg/middlewares/tracing"
"github.com/containous/traefik/v2/pkg/rules" "github.com/containous/traefik/v2/pkg/rules"
"github.com/containous/traefik/v2/pkg/server/internal"
"github.com/containous/traefik/v2/pkg/server/middleware" "github.com/containous/traefik/v2/pkg/server/middleware"
"github.com/containous/traefik/v2/pkg/server/provider"
) )
const ( const (
@ -121,7 +121,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
} }
for routerName, routerConfig := range configs { for routerName, routerConfig := range configs {
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerName), log.Str(log.RouterName, routerName)) ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
logger := log.FromContext(ctxRouter) logger := log.FromContext(ctxRouter)
handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig) handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig)
@ -175,7 +175,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterInfo, routerName string) (http.Handler, error) { func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterInfo, routerName string) (http.Handler, error) {
var qualifiedNames []string var qualifiedNames []string
for _, name := range router.Middlewares { for _, name := range router.Middlewares {
qualifiedNames = append(qualifiedNames, internal.GetQualifiedName(ctx, name)) qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(ctx, name))
} }
router.Middlewares = qualifiedNames router.Middlewares = qualifiedNames
rm := m.modifierBuilder.Build(ctx, qualifiedNames) rm := m.modifierBuilder.Build(ctx, qualifiedNames)

View file

@ -10,7 +10,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/rules" "github.com/containous/traefik/v2/pkg/rules"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
tcpservice "github.com/containous/traefik/v2/pkg/server/service/tcp" tcpservice "github.com/containous/traefik/v2/pkg/server/service/tcp"
"github.com/containous/traefik/v2/pkg/tcp" "github.com/containous/traefik/v2/pkg/tcp"
traefiktls "github.com/containous/traefik/v2/pkg/tls" traefiktls "github.com/containous/traefik/v2/pkg/tls"
@ -112,7 +112,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
continue continue
} }
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName)) ctxRouter := log.With(provider.AddInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName))
logger := log.FromContext(ctxRouter) logger := log.FromContext(ctxRouter)
domains, err := rules.ParseDomains(routerHTTPConfig.Rule) domains, err := rules.ParseDomains(routerHTTPConfig.Rule)
@ -131,7 +131,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
if routerHTTPConfig.TLS != nil { if routerHTTPConfig.TLS != nil {
tlsOptionsName := routerHTTPConfig.TLS.Options tlsOptionsName := routerHTTPConfig.TLS.Options
if tlsOptionsName != defaultTLSConfigName { if tlsOptionsName != defaultTLSConfigName {
tlsOptionsName = internal.GetQualifiedName(ctxRouter, routerHTTPConfig.TLS.Options) tlsOptionsName = provider.GetQualifiedName(ctxRouter, routerHTTPConfig.TLS.Options)
} }
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName) tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName)
@ -180,7 +180,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
} }
for routerName, routerConfig := range configs { for routerName, routerConfig := range configs {
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerName), log.Str(log.RouterName, routerName)) ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
logger := log.FromContext(ctxRouter) logger := log.FromContext(ctxRouter)
if routerConfig.Service == "" { if routerConfig.Service == "" {
@ -226,7 +226,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
} }
if tlsOptionsName != defaultTLSConfigName { if tlsOptionsName != defaultTLSConfigName {
tlsOptionsName = internal.GetQualifiedName(ctxRouter, tlsOptionsName) tlsOptionsName = provider.GetQualifiedName(ctxRouter, tlsOptionsName)
} }
tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName) tlsConf, err := m.tlsManager.Get(defaultTLSStoreName, tlsOptionsName)

View file

@ -58,7 +58,7 @@ func (s *Server) Start(ctx context.Context) {
s.tcpEntryPoints.Start() s.tcpEntryPoints.Start()
s.watcher.Start() s.watcher.Start()
s.routinesPool.Go(s.listenSignals) s.routinesPool.GoCtx(s.listenSignals)
} }
// Wait blocks until the server shutdown. // Wait blocks until the server shutdown.
@ -90,7 +90,7 @@ func (s *Server) Close() {
stopMetricsClients() stopMetricsClients()
s.routinesPool.Cleanup() s.routinesPool.Stop()
signal.Stop(s.signals) signal.Stop(s.signals)
close(s.signals) close(s.signals)

View file

@ -3,6 +3,7 @@
package server package server
import ( import (
"context"
"os/signal" "os/signal"
"syscall" "syscall"
@ -13,10 +14,10 @@ func (s *Server) configureSignals() {
signal.Notify(s.signals, syscall.SIGUSR1) signal.Notify(s.signals, syscall.SIGUSR1)
} }
func (s *Server) listenSignals(stop chan bool) { func (s *Server) listenSignals(ctx context.Context) {
for { for {
select { select {
case <-stop: case <-ctx.Done():
return return
case sig := <-s.signals: case sig := <-s.signals:
if sig == syscall.SIGUSR1 { if sig == syscall.SIGUSR1 {

View file

@ -2,6 +2,8 @@
package server package server
import "context"
func (s *Server) configureSignals() {} func (s *Server) configureSignals() {}
func (s *Server) listenSignals(stop chan bool) {} func (s *Server) listenSignals(ctx context.Context) {}

View file

@ -22,7 +22,7 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/pipelining" "github.com/containous/traefik/v2/pkg/middlewares/pipelining"
"github.com/containous/traefik/v2/pkg/safe" "github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/server/cookie" "github.com/containous/traefik/v2/pkg/server/cookie"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/containous/traefik/v2/pkg/server/service/loadbalancer/mirror" "github.com/containous/traefik/v2/pkg/server/service/loadbalancer/mirror"
"github.com/containous/traefik/v2/pkg/server/service/loadbalancer/wrr" "github.com/containous/traefik/v2/pkg/server/service/loadbalancer/wrr"
"github.com/vulcand/oxy/roundrobin" "github.com/vulcand/oxy/roundrobin"
@ -63,8 +63,8 @@ type Manager struct {
func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, responseModifier func(*http.Response) error) (http.Handler, error) { func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, responseModifier func(*http.Response) error) (http.Handler, error) {
ctx := log.With(rootCtx, log.Str(log.ServiceName, serviceName)) ctx := log.With(rootCtx, log.Str(log.ServiceName, serviceName))
serviceName = internal.GetQualifiedName(ctx, serviceName) serviceName = provider.GetQualifiedName(ctx, serviceName)
ctx = internal.AddProviderInContext(ctx, serviceName) ctx = provider.AddInContext(ctx, serviceName)
conf, ok := m.configs[serviceName] conf, ok := m.configs[serviceName]
if !ok { if !ok {

View file

@ -9,7 +9,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/containous/traefik/v2/pkg/testhelpers" "github.com/containous/traefik/v2/pkg/testhelpers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -336,7 +336,7 @@ func TestManager_Build(t *testing.T) {
ctx := context.Background() ctx := context.Background()
if len(test.providerName) > 0 { if len(test.providerName) > 0 {
ctx = internal.AddProviderInContext(ctx, "foobar@"+test.providerName) ctx = provider.AddInContext(ctx, "foobar@"+test.providerName)
} }
_, err := manager.BuildHTTP(ctx, test.serviceName, nil) _, err := manager.BuildHTTP(ctx, test.serviceName, nil)

View file

@ -9,7 +9,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/containous/traefik/v2/pkg/tcp" "github.com/containous/traefik/v2/pkg/tcp"
) )
@ -27,8 +27,8 @@ func NewManager(conf *runtime.Configuration) *Manager {
// BuildTCP Creates a tcp.Handler for a service configuration. // BuildTCP Creates a tcp.Handler for a service configuration.
func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Handler, error) { func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Handler, error) {
serviceQualifiedName := internal.GetQualifiedName(rootCtx, serviceName) serviceQualifiedName := provider.GetQualifiedName(rootCtx, serviceName)
ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName) ctx := provider.AddInContext(rootCtx, serviceQualifiedName)
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName)) ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
conf, ok := m.configs[serviceQualifiedName] conf, ok := m.configs[serviceQualifiedName]

View file

@ -6,7 +6,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/runtime" "github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/server/internal" "github.com/containous/traefik/v2/pkg/server/provider"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -184,7 +184,7 @@ func TestManager_BuildTCP(t *testing.T) {
ctx := context.Background() ctx := context.Background()
if len(test.providerName) > 0 { if len(test.providerName) > 0 {
ctx = internal.AddProviderInContext(ctx, "foobar@"+test.providerName) ctx = provider.AddInContext(ctx, "foobar@"+test.providerName)
} }
handler, err := manager.BuildTCP(ctx, test.serviceName) handler, err := manager.BuildTCP(ctx, test.serviceName)