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

Compare commits

..

35 Commits

Author SHA1 Message Date
Kevin Pollet
76e35a09b7 Prepare release v2.3.4 2020-11-24 17:06:04 +01:00
SkapiN
d2c1d39d42 Fix clusters option in ECS provider documentation 2020-11-24 14:50:03 +01:00
Harold Ozouf
e9cccf6504 Do not evaluate templated URL in redirectRegex middleware 2020-11-24 14:16:03 +01:00
Ludovic Fernandez
1c505903ff fix: invalid slice parsing. 2020-11-24 09:40:03 +01:00
Ludovic Fernandez
53ed8e04ae Update go-acme/lego to v4.1.2 2020-11-23 12:00:03 +01:00
Romain
be0845af02 Apply labelSelector as a TweakListOptions for Kubernetes informers 2020-11-20 00:18:04 +01:00
Ludovic Fernandez
f83a57b3da Prepare release v2.3.3 2020-11-19 18:31:09 +01:00
Kevin Pollet
08264749f0 Update Yaegi to v0.9.7 2020-11-19 17:56:03 +01:00
Harold Ozouf
a75819cae3 Filter out Helm secrets from informer caches
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
2020-11-19 14:32:03 +01:00
Ivor Scott
9fb32a47ca Fix grammar in kubernetes ingress controller documentation 2020-11-19 10:04:04 +01:00
Harold Ozouf
4f43c9ebb4 Fix missing allow-empty tag on ECS and Consul Catalog providers
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
2020-11-19 00:12:03 +01:00
Harold Ozouf
9177982334 Fix consul catalog panic when health and services are not in sync
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
2020-11-17 17:30:03 +01:00
Yoan Blanc
52eeff9f9f fix: consulcatalog to update before the first interval 2020-11-16 20:44:04 +01:00
Petyo Kunchev
0fcccd35ff /bin/bash replaced with /usr/bin/env bash to match other scripts 2020-11-16 15:38:04 +01:00
Douglas De Toni Machado
598dcf6b62 Improve service name lookup on TCP routers 2020-11-13 12:48:04 +01:00
Alexander Wellbrock
459200dd01 Forwardauth headers 2020-11-10 17:50:04 +01:00
james426759
af22cabc6f Fix docs for TLS 2020-11-10 17:28:04 +01:00
Alessandro Chitolina
920e82f11a fix: translate configured server port into correct mapped host port 2020-11-09 17:12:05 +01:00
Ludovic Fernandez
9bdf9e1e02 Update Yaegi to v0.9.5 2020-11-08 23:42:03 +01:00
Ludovic Fernandez
3a45f05e36 Update go-acme/lego to v4.1.0 2020-11-08 23:24:04 +01:00
Neil McAllister
8e3e387be7 Fix Traefik Proxy product nav in docs 2020-11-06 21:56:03 +01:00
Ludovic Fernandez
267d0b7b5a chore: update linter. 2020-11-06 09:26:03 +01:00
Kevin Pollet
f5b290b093 Add ECS menu to dynamic config reference 2020-11-03 17:40:04 +01:00
Michael
4ea1c98ac9 Improve anonymize configuration 2020-10-30 12:44:05 +01:00
Tristan Weil
db007efe00 Ignore errors when setting keepalive period is not supported by the system 2020-10-28 15:32:04 +01:00
Jean-Baptiste Doumenjou
a0c02f62a3 fix: exclude protected link from doc verify 2020-10-27 18:34:04 +01:00
Jean-Baptiste Doumenjou
ff7b814edc fix documentation 2020-10-27 12:46:04 +01:00
Ludovic Fernandez
ea459e9af0 fix: update Yaegi to v0.9.4 2020-10-23 11:30:04 +02:00
Andrii Dembitskyi
2dd5a53db2 Add missed tls config for yaml example 2020-10-23 11:00:05 +02:00
Romain
fc97ea7ee0 Use timezone without daylight saving time for logger formatter tests
Co-authored-by: jbdoumenjou <925513+jbdoumenjou@users.noreply.github.com>
2020-10-22 19:52:04 +02:00
Kevin Crawley
582d2540af add links to contributors guide 2020-10-22 12:08:05 +02:00
Tom Matthews
6ad79dcd45 Clarify time-based field units 2020-10-22 11:36:03 +02:00
Tom Matthews
721896ba70 Resolve broken URLs causing make docs to fail 2020-10-20 23:02:04 +02:00
Yeri Pratama
228270414c fix typo in providers overview documentation 2020-10-20 19:02:04 +02:00
Romain
2683df7b5b Fix ingress documentation 2020-10-20 14:16:04 +02:00
96 changed files with 2528 additions and 806 deletions

View File

@@ -54,7 +54,10 @@
"nestif", # Too many false-positive.
"noctx", # Too strict
"exhaustive", # Too strict
"nlreturn", # Too strict
"nlreturn", # Not relevant
"wrapcheck", # Too strict
"tparallel", # Not relevant
"exhaustivestruct", # Not relevant
]
[issues]

View File

@@ -1,3 +1,47 @@
## [v2.3.4](https://github.com/traefik/traefik/tree/v2.3.4) (2020-11-24)
[All Commits](https://github.com/traefik/traefik/compare/v2.3.3...v2.3.4)
**Bug fixes:**
- **[acme]** Update go-acme/lego to v4.1.2 ([#7577](https://github.com/traefik/traefik/pull/7577) by [ldez](https://github.com/ldez))
- **[k8s,k8s/crd,k8s/ingress]** Apply labelSelector as a TweakListOptions for Kubernetes informers ([#7521](https://github.com/traefik/traefik/pull/7521) by [rtribotte](https://github.com/rtribotte))
- **[middleware]** Do not evaluate templated URL in redirectRegex middleware ([#7573](https://github.com/traefik/traefik/pull/7573) by [jspdown](https://github.com/jspdown))
- **[provider]** fix: invalid slice parsing. ([#7583](https://github.com/traefik/traefik/pull/7583) by [ldez](https://github.com/ldez))
**Documentation:**
- **[ecs]** Fix clusters option in ECS provider documentation ([#7586](https://github.com/traefik/traefik/pull/7586) by [skapin](https://github.com/skapin))
## [v2.3.3](https://github.com/traefik/traefik/tree/v2.3.3) (2020-11-19)
[All Commits](https://github.com/traefik/traefik/compare/v2.3.2...v2.3.3)
**Bug fixes:**
- **[acme]** Update go-acme/lego to v4.1.0 ([#7526](https://github.com/traefik/traefik/pull/7526) by [ldez](https://github.com/ldez))
- **[consulcatalog,ecs]** Fix missing allow-empty tag on ECS and Consul Catalog providers ([#7561](https://github.com/traefik/traefik/pull/7561) by [jspdown](https://github.com/jspdown))
- **[consulcatalog]** consulcatalog to update before the first interval ([#7514](https://github.com/traefik/traefik/pull/7514) by [greut](https://github.com/greut))
- **[consulcatalog]** Fix consul catalog panic when health and services are not in sync ([#7558](https://github.com/traefik/traefik/pull/7558) by [jspdown](https://github.com/jspdown))
- **[ecs]** Translate configured server port into correct mapped host port ([#7480](https://github.com/traefik/traefik/pull/7480) by [alekitto](https://github.com/alekitto))
- **[k8s,k8s/crd,k8s/ingress]** Filter out Helm secrets from informer caches ([#7562](https://github.com/traefik/traefik/pull/7562) by [jspdown](https://github.com/jspdown))
- **[plugins]** Update Yaegi to v0.9.5 ([#7527](https://github.com/traefik/traefik/pull/7527) by [ldez](https://github.com/ldez))
- **[plugins]** Update Yaegi to v0.9.7 ([#7569](https://github.com/traefik/traefik/pull/7569) by [kevinpollet](https://github.com/kevinpollet))
- **[plugins]** Update Yaegi to v0.9.4 ([#7451](https://github.com/traefik/traefik/pull/7451) by [ldez](https://github.com/ldez))
- **[tcp]** Ignore errors when setting keepalive period is not supported by the system ([#7410](https://github.com/traefik/traefik/pull/7410) by [tristan-weil](https://github.com/tristan-weil))
- **[tcp]** Improve service name lookup on TCP routers ([#7370](https://github.com/traefik/traefik/pull/7370) by [ddtmachado](https://github.com/ddtmachado))
- Improve anonymize configuration ([#7482](https://github.com/traefik/traefik/pull/7482) by [mmatur](https://github.com/mmatur))
**Documentation:**
- **[ecs]** Add ECS menu to dynamic config reference ([#7501](https://github.com/traefik/traefik/pull/7501) by [kevinpollet](https://github.com/kevinpollet))
- **[k8s,k8s/ingress]** Fix ingress documentation ([#7424](https://github.com/traefik/traefik/pull/7424) by [rtribotte](https://github.com/rtribotte))
- **[k8s]** fix documentation ([#7469](https://github.com/traefik/traefik/pull/7469) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[k8s]** Fix grammar in kubernetes ingress controller documentation ([#7565](https://github.com/traefik/traefik/pull/7565) by [ivorscott](https://github.com/ivorscott))
- **[logs]** Clarify time-based field units ([#7447](https://github.com/traefik/traefik/pull/7447) by [tomtastic](https://github.com/tomtastic))
- **[middleware]** Forwardauth headers ([#7506](https://github.com/traefik/traefik/pull/7506) by [w4tsn](https://github.com/w4tsn))
- **[provider]** fix typo in providers overview documentation ([#7441](https://github.com/traefik/traefik/pull/7441) by [pirey](https://github.com/pirey))
- **[tls]** Fix docs for TLS ([#7541](https://github.com/traefik/traefik/pull/7541) by [james426759](https://github.com/james426759))
- fix: exclude protected link from doc verify ([#7477](https://github.com/traefik/traefik/pull/7477) by [jbdoumenjou](https://github.com/jbdoumenjou))
- Add missed tls config for yaml example ([#7450](https://github.com/traefik/traefik/pull/7450) by [andrew-demb](https://github.com/andrew-demb))
- Resolve broken URLs causing make docs to fail ([#7444](https://github.com/traefik/traefik/pull/7444) by [tomtastic](https://github.com/tomtastic))
- Fix Traefik Proxy product nav in docs ([#7523](https://github.com/traefik/traefik/pull/7523) by [PCM2](https://github.com/PCM2))
- add links to contributors guide ([#7435](https://github.com/traefik/traefik/pull/7435) by [notsureifkevin](https://github.com/notsureifkevin))
## [v2.3.2](https://github.com/traefik/traefik/tree/v2.3.2) (2020-10-19)
[All Commits](https://github.com/traefik/traefik/compare/v2.3.1...v2.3.2)

View File

@@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \
&& chmod +x /usr/local/bin/go-bindata
# Download golangci-lint binary to bin folder in $GOPATH
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.31.0
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.32.2
# Download misspell binary to bin folder in $GOPATH
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4

View File

@@ -11,75 +11,20 @@
* Ludovic Fernandez [@ldez](https://github.com/ldez)
* Julien Salleyron [@juliens](https://github.com/juliens)
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
* Marco Jantke [@mjantke](https://github.com/mjeri)
* Michaël Matur [@mmatur](https://github.com/mmatur)
* Gérald Croës [@geraldcroes](https://github.com/geraldcroes)
* Jean-Baptiste Doumenjou [@jbdoumenjou](https://github.com/jbdoumenjou)
* Mathieu Lonjaret [@mpl](https://github.com/mpl)
* Romain Tribotté [@rtribotte](https://github.com/rtribotte)
## Contributions Daily Meeting
## Issue Triage
* 3 Maintainers should attend to a Contributions Daily Meeting where we sort and label new issues ([is:issue label:status/0-needs-triage](https://github.com/traefik/traefik/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Astatus%2F0-needs-triage+)), and review every Pull Requests
* Every pull request should be checked during the Contributions Daily Meeting
* Even if its already assigned
* Even PR labelled with `contributor/waiting-for-corrections` or `contributor/waiting-for-feedback`
* Issues labeled with `priority/P0` and `priority/P1` should be assigned.
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
* During the Contributions Daily Meeting
* By an assigned maintainer
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, Discourse, etc)
Issues and PRs are triaged daily and the process for triaging may be found under [triaging issues](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md) in our [contributors guide repository](https://github.com/traefik/contributors-guide).
## PR review process:
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
* From `1` to `2`: 1 comment that says “design LGTM” (by a senior maintainer).
* From `2` to `3`: 3 LGTM approvals by any maintainer.
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
* If a PR has been implemented in pair programming, one peer's LGTM goes into the review for free
* Amending someone else's pull request is authorized only in emergency, if a rebase is needed, or if the initial contributor is silent
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
## Bots
### [Myrmica Lobicornis](https://github.com/traefik/lobicornis/)
Update and Merge Pull Request.
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
By default, a squash-rebase merge will be carried out.
To preserve commits, add `bot/merge-method-rebase` before `status/3-needs-merge`.
The status `status/4-merge-in-progress` is only used by the bot.
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
This label is used when:
* Updating the vendors from previously reviewed PRs
* Merging branches into the master
* Preparing the release
### [Myrmica Bibikoffi](https://github.com/traefik/bibikoffi/)
* closes stale issues [cron]
* use some criterion as number of days between creation, last update, labels, ...
### [Myrmica Aloba](https://github.com/traefik/aloba)
Manage GitHub labels.
* Add labels on new PR [GitHub WebHook]
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
* Weekly report of PR status on Slack (CaptainPR) [cron]
The process for reviewing PRs may be found under [review guidelines](https://github.com/traefik/contributors-guide/blob/master/review_guidelines.md) in our contributors guide repository.
## Labels

View File

@@ -5,41 +5,5 @@ A Quick Guide for Efficient Contributions
So you've decided to improve Traefik?
Thank You!
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 through the classic pitfalls to make sure everything is right.
## Title
The title must be short and descriptive. (~60 characters)
## Description
Follow the [pull request template](https://github.com/traefik/traefik/blob/master/.github/PULL_REQUEST_TEMPLATE.md) as much as possible.
Explain the conditions which led you to write this PR: give us context.
The context should lead to something, an idea or a problem that youre facing.
Remain clear and concise.
Take time to polish the format of your message so we'll enjoy reading it and working on it.
Help the readers focus on what matters, and help them understand the structure of your message (see the [Github Markdown Syntax](https://help.github.com/articles/github-flavored-markdown)).
## PR Content
- Make it small.
- One feature per Pull Request.
- Write useful descriptions and titles.
- Avoid re-formatting code that is not on the path of your PR.
- Make sure the [code builds](building-testing.md).
- Make sure [all tests pass](building-testing.md).
- Add tests.
- Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial).
!!! 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.
!!! tip "10 Tips for Better Pull Requests"
We enjoyed this article, maybe you will too! [10 tips for better pull requests](https://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/).
Please review the [guidelines on creating PRs](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md) for Traefik in our [contributors guide repository](https://github.com/traefik/contributors-guide).

View File

@@ -322,8 +322,9 @@ For complete details, refer to your provider's _Additional configuration_ link.
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |

View File

@@ -64,7 +64,7 @@ tls:
!!! important "Restriction"
Any store definition other than the default one (named `default`) will be ignored,
and there is thefore only one globally available TLS store.
and there is therefore only one globally available TLS store.
In the `tls.certificates` section, a list of stores can then be specified to indicate where the certificates should be stored:

View File

@@ -61,6 +61,18 @@ http:
address: "https://example.com/auth"
```
## Forward-Request Headers
The following request properties are provided to the forward-auth target endpoint as `X-Forwarded-` headers.
| Property | Forward-Request Header |
|-------------------|------------------------|
| HTTP Method | X-Forwarded-Method |
| Protocol | X-Forwarded-Proto |
| Host | X-Forwarded-Host |
| Request URI | X-Forwarded-Uri |
| Source IP-Address | X-Forwarded-For |
## Configuration Options
### `address`

View File

@@ -74,7 +74,7 @@ The available filters are:
- `statusCodes`, to limit the access logs to requests with a status codes in the specified range
- `retryAttempts`, to keep the access logs when at least one retry has happened
- `minDuration`, to keep access logs when requests take longer than the specified duration
- `minDuration`, to keep access logs when requests take longer than the specified duration (provided in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
```toml tab="File (TOML)"
# Configuring Multiple Filters
@@ -198,7 +198,7 @@ accessLog:
| `RequestScheme` | The HTTP scheme requested `http` or `https`. |
| `RequestLine` | `RequestMethod` + `RequestPath` + `RequestProtocol` |
| `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. |
| `OriginDuration` | The time taken by the origin server ('upstream') to return its response. |
| `OriginDuration` | The time taken (in nanoseconds) by the origin server ('upstream') to return its response. |
| `OriginContentSize` | The content length specified by the origin server, or 0 if unspecified. |
| `OriginStatus` | The HTTP status code returned by the origin server. If the request was handled by this Traefik instance (e.g. with a redirect), then this value will be absent. |
| `OriginStatusLine` | `OriginStatus` + Status code explanation |
@@ -207,7 +207,7 @@ accessLog:
| `DownstreamContentSize` | The number of bytes in the response entity returned to the client. This is in addition to the "Content-Length" header, which may be present in the origin response. |
| `RequestCount` | The number of requests received since the Traefik instance started. |
| `GzipRatio` | The response body compression ratio achieved. |
| `Overhead` | The processing time overhead caused by Traefik. |
| `Overhead` | The processing time overhead (in nanoseconds) caused by Traefik. |
| `RetryAttempts` | The amount of attempts the request was retried. |
## Log Rotation

View File

@@ -144,8 +144,8 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
Accessing the Docker API without any restriction is a security concern:
If Traefik is attacked, then the attacker might get access to the underlying host.
{: #security-note }
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/#docker-daemon-attack-surface)):
!!! quote
[...] only **trusted** users should be allowed to control your Docker daemon [...]

View File

@@ -13,18 +13,15 @@ Attach labels to your ECS containers and let Traefik do the rest!
```toml tab="File (TOML)"
[providers.ecs]
clusters = ["default"]
```
```yaml tab="File (YAML)"
providers:
ecs:
clusters:
- default
ecs: {}
```
```bash tab="CLI"
--providers.ecs.clusters=default
--providers.ecs=true
```
## Policy
@@ -90,7 +87,7 @@ _Optional, Default=["default"]_
```toml tab="File (TOML)"
[providers.ecs]
cluster = ["default"]
clusters = ["default"]
# ...
```

View File

@@ -177,26 +177,32 @@ _Optional,Default: empty (process all resources)_
```toml tab="File (TOML)"
[providers.kubernetesCRD]
labelselector = "A and not B"
labelselector = "app=traefik"
# ...
```
```yaml tab="File (YAML)"
providers:
kubernetesCRD:
labelselector: "A and not B"
labelselector: "app=traefik"
# ...
```
```bash tab="CLI"
--providers.kubernetescrd.labelselector="A and not B"
--providers.kubernetescrd.labelselector="app=traefik"
```
By default, Traefik processes all resource objects in the configured namespaces.
A label selector can be defined to filter on specific resource objects only.
A label selector can be defined to filter on specific resource objects only,
this will apply only on Traefik [Custom Resources](../routing/providers/kubernetes-crd.md#custom-resource-definition-crd)
and has no effect on Kubernetes `Secrets`, `Endpoints` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
!!! warning
As the LabelSelector is applied to all Traefik Custom Resources, they all must match the filter.
### `ingressClass`
_Optional, Default: empty_

View File

@@ -4,7 +4,7 @@ The Kubernetes Ingress Controller.
{: .subtitle }
The Traefik Kubernetes Ingress provider is a Kubernetes Ingress controller; that is to say,
it manages access to a cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification.
it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification.
## Routing Configuration
@@ -212,23 +212,23 @@ _Optional,Default: empty (process all Ingresses)_
```toml tab="File (TOML)"
[providers.kubernetesIngress]
labelSelector = "A and not B"
labelSelector = "app=traefik"
# ...
```
```yaml tab="File (YAML)"
providers:
kubernetesIngress:
labelselector: "A and not B"
labelselector: "app=traefik"
# ...
```
```bash tab="CLI"
--providers.kubernetesingress.labelselector="A and not B"
--providers.kubernetesingress.labelselector="app=traefik"
```
By default, Traefik processes all Ingress objects in the configured namespaces.
A label selector can be defined to filter on specific Ingress objects only.
By default, Traefik processes all `Ingress` objects in the configured namespaces.
A label selector can be defined to filter on specific `Ingress` objects only.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.

View File

@@ -46,7 +46,7 @@ followed by the provider name to the object name.
On the other hand, if you, say, declare a middleware as a Custom Resource in Kubernetes and use the non-crd Ingress objects,
you'll have to add the Kubernetes namespace of the middleware to the annotation like this `<middleware-namespace>-<middleware-name>@kubernetescrd`.
!!! abstract "Referencing a Traedik dynamic configuration object from Another Provider"
!!! abstract "Referencing a Traefik dynamic configuration object from Another Provider"
Declaring the add-foo-prefix in the file provider.

View File

@@ -330,6 +330,9 @@ TLS key
`--providers.consul.username`:
KV Username
`--providers.consulcatalog`:
Enable ConsulCatalog backend with default settings. (Default: ```false```)
`--providers.consulcatalog.cache`:
Use local agent caching for catalog reads. (Default: ```false```)
@@ -435,6 +438,9 @@ Use the ip address from the bound port, rather than from the inner network. (Def
`--providers.docker.watch`:
Watch Docker Swarm events. (Default: ```true```)
`--providers.ecs`:
Enable AWS ECS backend with default settings. (Default: ```false```)
`--providers.ecs.accesskeyid`:
The AWS credentials access key to use for making requests

View File

@@ -303,6 +303,9 @@ Terminating status code (Default: ```503```)
`TRAEFIK_PROVIDERS_CONSUL`:
Enable Consul backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_CONSULCATALOG`:
Enable ConsulCatalog backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_CONSULCATALOG_CACHE`:
Use local agent caching for catalog reads. (Default: ```false```)
@@ -435,6 +438,9 @@ Use the ip address from the bound port, rather than from the inner network. (Def
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
Watch Docker Swarm events. (Default: ```true```)
`TRAEFIK_PROVIDERS_ECS`:
Enable AWS ECS backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_ECS_ACCESSKEYID`:
The AWS credentials access key to use for making requests

View File

@@ -212,8 +212,8 @@ If both TCP and UDP are wanted for the same port, two entryPoints definitions ar
```
```bash tab="CLI"
entrypoints.specificIPv4.address=192.168.2.7:8888
entrypoints.specificIPv6.address=[2001:db8::1]:8888
--entrypoints.specificIPv4.address=192.168.2.7:8888
--entrypoints.specificIPv6.address=[2001:db8::1]:8888
```
Full details for how to specify `address` can be found in [net.Listen](https://golang.org/pkg/net/#Listen) (and [net.Dial](https://golang.org/pkg/net/#Dial)) of the doc for go.
@@ -745,8 +745,8 @@ entryPoints:
```
```bash tab="CLI"
entrypoints.websecure.address=:443
entrypoints.websecure.http.middlewares=auth@file,strip@file
--entrypoints.websecure.address=:443
--entrypoints.websecure.http.middlewares=auth@file,strip@file
```
### TLS
@@ -792,13 +792,13 @@ entryPoints:
```
```bash tab="CLI"
entrypoints.websecure.address=:443
entrypoints.websecure.http.tls.options=foobar
entrypoints.websecure.http.tls.certResolver=leresolver
entrypoints.websecure.http.tls.domains[0].main=example.com
entrypoints.websecure.http.tls.domains[0].sans=foo.example.com,bar.example.com
entrypoints.websecure.http.tls.domains[1].main=test.com
entrypoints.websecure.http.tls.domains[1].sans=foo.test.com,bar.test.com
--entrypoints.websecure.address=:443
--entrypoints.websecure.http.tls.options=foobar
--entrypoints.websecure.http.tls.certResolver=leresolver
--entrypoints.websecure.http.tls.domains[0].main=example.com
--entrypoints.websecure.http.tls.domains[0].sans=foo.example.com,bar.example.com
--entrypoints.websecure.http.tls.domains[1].main=test.com
--entrypoints.websecure.http.tls.domains[1].sans=foo.test.com,bar.test.com
```
??? example "Let's Encrypt"
@@ -821,6 +821,6 @@ entrypoints.websecure.http.tls.domains[1].sans=foo.test.com,bar.test.com
```
```bash tab="CLI"
entrypoints.websecure.address=:443
entrypoints.websecure.http.tls.certResolver=leresolver
--entrypoints.websecure.address=:443
--entrypoints.websecure.http.tls.certResolver=leresolver
```

View File

@@ -228,6 +228,7 @@ http:
to-whoami-tcp:
service: whoami-tcp
rule: HostSNI(`whoami-tcp.example.com`)
tls: {}
services:
whoami-tcp:

View File

@@ -10,7 +10,7 @@ Attach labels to your containers and let Traefik do the rest!
!!! info "labels"
- labels are case insensitive.
- The complete list of labels can be found [the reference page](../../reference/dynamic-configuration/ecs.md)
- The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/ecs.md).
### General

View File

@@ -619,7 +619,7 @@ Register the `Middleware` [kind](../../reference/dynamic-configuration/kubernete
!!! important "Cross-provider namespace"
As Kubernetes also has its own notion of namespace, one should not confuse the kubernetes namespace of a resource
(in the reference to the middleware) with the [provider namespace](../../middlewares/overview.md#provider-namespace),
(in the reference to the middleware) with the [provider namespace](../../providers/overview.md#provider-namespace),
when the definition of the middleware comes from another provider.
In this context, specifying a namespace when referring to the resource does not make any sense, and will be ignored.
Additionally, when you want to reference a Middleware from the CRD Provider,
@@ -1456,8 +1456,7 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
If the optional `namespace` attribute is not set, the configuration will be applied with the namespace of the IngressRoute.
Additionally, when the definition of the TLS option is from another provider,
the cross-provider syntax (`middlewarename@provider`) should be used to refer to the TLS option,
just as in the [middleware case](../../middlewares/overview.md#provider-namespace).
the cross-provider [syntax](../../providers/overview.md#provider-namespace) (`middlewarename@provider`) should be used to refer to the TLS option.
Specifying a namespace attribute in this case would not make any sense, and will be ignored.
### Kind: `TLSStore`
@@ -1490,7 +1489,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
| Ref | Attribute | Purpose |
|-----|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `secretName` | The name of the referenced Kubernetes [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) that holds the default certificate for the store. |
| [1] | `secretName` | The name of the referenced Kubernetes [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) that holds the default certificate for the store. |
??? example "Declaring and referencing a TLSStore"

View File

@@ -114,16 +114,11 @@ which in turn will create the resulting routers, services, handlers, etc.
- name: traefik
image: traefik:v2.3
args:
- --log.level=DEBUG
- --api
- --api.insecure
- --entrypoints.web.address=:80
- --providers.kubernetesingress
ports:
- name: web
containerPort: 80
- name: admin
containerPort: 8080
---
apiVersion: v1
@@ -139,10 +134,6 @@ which in turn will create the resulting routers, services, handlers, etc.
port: 80
name: web
targetPort: 80
- protocol: TCP
port: 8080
name: admin
targetPort: 8080
```
```yaml tab="Whoami"
@@ -340,27 +331,380 @@ Please see [this documentation](https://kubernetes.io/docs/concepts/services-net
## TLS
### Communication Between Traefik and Pods
### Enabling TLS via HTTP Options on Entrypoint
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
Although Traefik will connect directly to the endpoints (pods),
it still checks the service port to see if TLS communication is required.
TLS can be enabled through the [HTTP options](../entrypoints.md#tls) of an Entrypoint:
There are 3 ways to configure Traefik to use https to communicate with pods:
```bash tab="CLI"
# Static configuration
--entrypoints.websecure.address=:443
--entrypoints.websecure.http.tls
```
1. If the service port defined in the ingress spec is `443` (note that you can still use `targetPort` to use a different port on your pod).
1. If the service port defined in the ingress spec has a name that starts with https (such as `https-api`, `https-web` or just `https`).
1. If the ingress spec includes the annotation `traefik.ingress.kubernetes.io/service.serversscheme: https`.
```toml tab="File (TOML)"
# Static configuration
[entryPoints.websecure]
address = ":443"
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS,
and will connect via TLS automatically.
[entryPoints.websecure.http.tls]
```
!!! info
```yaml tab="File (YAML)"
# Static configuration
entryPoints:
websecure:
address: ':443'
http:
tls: {}
```
This way, any Ingress attached to this Entrypoint will have TLS termination by default.
??? example "Configuring Kubernetes Ingress Controller with TLS on Entrypoint"
Please note that by enabling TLS communication between traefik and your pods,
you will have to have trusted certificates that have the proper trust chain and IP subject name.
If this is not an option, you may need to skip TLS certificate verification.
See the [insecureSkipVerify](../../routing/overview.md#insecureskipverify) setting for more details.
```yaml tab="RBAC"
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
- ingressclasses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: default
```
```yaml tab="Ingress"
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: myingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
rules:
- host: example.com
http:
paths:
- path: /bar
backend:
serviceName: whoami
servicePort: 80
- path: /foo
backend:
serviceName: whoami
servicePort: 80
```
```yaml tab="Traefik"
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.3
args:
- --entrypoints.websecure.address=:443
- --entrypoints.websecure.http.tls
- --providers.kubernetesingress
ports:
- name: websecure
containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
name: traefik
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- protocol: TCP
port: 443
name: websecure
targetPort: 443
```
```yaml tab="Whoami"
kind: Deployment
apiVersion: apps/v1
metadata:
name: whoami
labels:
app: traefiklabs
name: whoami
spec:
replicas: 2
selector:
matchLabels:
app: traefiklabs
task: whoami
template:
metadata:
labels:
app: traefiklabs
task: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- name: http
port: 80
selector:
app: traefiklabs
task: whoami
```
### Enabling TLS via Annotations
To enable TLS on the underlying router created from an Ingress, one should configure it through annotations:
```yaml
traefik.ingress.kubernetes.io/router.tls: "true"
```
For more options, please refer to the available [annotations](#on-ingress).
??? example "Configuring Kubernetes Ingress Controller with TLS"
```yaml tab="RBAC"
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
- ingressclasses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: default
```
```yaml tab="Ingress"
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: myingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: true
spec:
rules:
- host: example.com
http:
paths:
- path: /bar
backend:
serviceName: whoami
servicePort: 80
- path: /foo
backend:
serviceName: whoami
servicePort: 80
```
```yaml tab="Traefik"
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.3
args:
- --entrypoints.websecure.address=:443
- --providers.kubernetesingress
ports:
- name: websecure
containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
name: traefik
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- protocol: TCP
port: 443
name: websecure
targetPort: 443
```
```yaml tab="Whoami"
kind: Deployment
apiVersion: apps/v1
metadata:
name: whoami
labels:
app: traefiklabs
name: whoami
spec:
replicas: 2
selector:
matchLabels:
app: traefiklabs
task: whoami
template:
metadata:
labels:
app: traefiklabs
task: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- name: http
port: 80
selector:
app: traefiklabs
task: whoami
```
### Certificates Management
@@ -382,7 +726,9 @@ and will connect via TLS automatically.
backend:
serviceName: service1
servicePort: 80
# Only selects which certificate(s) should be loaded from the secret, in order to terminate TLS.
# Doesn't enable TLS for that ingress (hence for the underlying router).
# Please see the TLS annotations on ingress made for that purpose.
tls:
- secretName: supersecret
```
@@ -405,6 +751,28 @@ TLS certificates can be managed in Secrets objects.
Only TLS certificates provided by users can be stored in Kubernetes Secrets.
[Let's Encrypt](../../https/acme.md) certificates cannot be managed in Kubernetes Secrets yet.
### Communication Between Traefik and Pods
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
Although Traefik will connect directly to the endpoints (pods),
it still checks the service port to see if TLS communication is required.
There are 3 ways to configure Traefik to use https to communicate with pods:
1. If the service port defined in the ingress spec is `443` (note that you can still use `targetPort` to use a different port on your pod).
1. If the service port defined in the ingress spec has a name that starts with https (such as `https-api`, `https-web` or just `https`).
1. If the ingress spec includes the annotation `traefik.ingress.kubernetes.io/service.serversscheme: https`.
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS,
and will connect via TLS automatically.
!!! info
Please note that by enabling TLS communication between traefik and your pods,
you will have to have trusted certificates that have the proper trust chain and IP subject name.
If this is not an option, you may need to skip TLS certificate verification.
See the [insecureSkipVerify](../../routing/overview.md#insecureskipverify) setting for more details.
## Global Default Backend Ingresses
Ingresses can be created that look like the following:

View File

@@ -191,6 +191,7 @@ nav:
- 'Docker': 'reference/dynamic-configuration/docker.md'
- 'Kubernetes CRD': 'reference/dynamic-configuration/kubernetes-crd.md'
- 'Consul Catalog': 'reference/dynamic-configuration/consul-catalog.md'
- 'ECS': 'reference/dynamic-configuration/ecs.md'
- 'KV': 'reference/dynamic-configuration/kv.md'
- 'Marathon': 'reference/dynamic-configuration/marathon.md'
- 'Rancher': 'reference/dynamic-configuration/rancher.md'
- 'KV': 'reference/dynamic-configuration/kv.md'

View File

@@ -22,7 +22,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \
--alt_ignore="/traefikproxy-vertical-logo-color.svg/" \
--http_status_ignore="0,500,501,503" \
--file_ignore="/404.html/" \
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/" \
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/" \
'{}' 1>/dev/null
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration

View File

@@ -37,7 +37,7 @@
<div class="dmi-image proxy">
<img src="{{ 'assets/images/traefik-proxy-logo.svg' | url }}" alt="Traefik Proxy" />
</div>
<a class="dmi-details" href="https://doc.traefik.io/traefik/">
<a class="dmi-details" href="https://traefik.io/traefik/">
<div class="dmi-title">Traefik Proxy</div>
<div class="dmi-description">
Expose, Secure and Monitor your modern applications

6
go.mod
View File

@@ -35,7 +35,7 @@ require (
github.com/fatih/structs v1.1.0
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
github.com/go-acme/lego/v4 v4.0.1
github.com/go-acme/lego/v4 v4.1.2
github.com/go-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
github.com/golang/protobuf v1.3.4
@@ -72,8 +72,8 @@ require (
github.com/stretchr/testify v1.6.1
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/tinylib/msgp v1.0.2 // indirect
github.com/traefik/paerser v0.1.0
github.com/traefik/yaegi v0.9.3
github.com/traefik/paerser v0.1.1
github.com/traefik/yaegi v0.9.7
github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2

13
go.sum
View File

@@ -275,8 +275,8 @@ github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLy
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego/v4 v4.0.1 h1:vPwbTYfw5+fOaON9rWCN43iNrPw5cdJBhNMnA8oxBTM=
github.com/go-acme/lego/v4 v4.0.1/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
github.com/go-acme/lego/v4 v4.1.2 h1:1zROppXkTbAIh7J7AydGD3dFICLIocucJY1NTH/wB64=
github.com/go-acme/lego/v4 v4.1.2/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -764,10 +764,10 @@ github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/traefik/paerser v0.1.0 h1:B4v1tbvd8YnHsA7spwHKEWJoGrRP+2jYpIozsCMHhl0=
github.com/traefik/paerser v0.1.0/go.mod h1:yYnAgdEC2wJH5CgG75qGWC8SsFDEapg09o9RrA6FfrE=
github.com/traefik/yaegi v0.9.3 h1:R9PZt0lB2LqspX4hGUQLq+c4sWBVVHPSKN7AxHnEs5E=
github.com/traefik/yaegi v0.9.3/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk=
github.com/traefik/paerser v0.1.1 h1:Suj0iA4hTAV6E4Dh5/++TXAj5u6iTwydBlFssIUz+9w=
github.com/traefik/paerser v0.1.1/go.mod h1:yYnAgdEC2wJH5CgG75qGWC8SsFDEapg09o9RrA6FfrE=
github.com/traefik/yaegi v0.9.7 h1:CbeKjEhy3DoSC8xC4TQF2Mhmd7u3Cjqluz1//x6Vtcs=
github.com/traefik/yaegi v0.9.7/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk=
github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE=
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
@@ -1108,6 +1108,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

View File

@@ -3,6 +3,8 @@ kind: Ingress
metadata:
name: test.ingress
namespace: default
labels:
app: traefik
spec:
rules:

View File

@@ -3,6 +3,8 @@ kind: IngressRoute
metadata:
name: test.route
namespace: default
labels:
app: traefik
spec:
entryPoints:

View File

@@ -3,6 +3,8 @@ kind: TLSOption
metadata:
name: mytlsoption
namespace: default
labels:
app: traefik
spec:
minVersion: VersionTLS12

View File

@@ -3,6 +3,8 @@ kind: TLSStore
metadata:
name: mytlsstore
namespace: default
labels:
app: traefik
spec:
defaultCertificate:

View File

@@ -50,6 +50,8 @@ kind: IngressRoute
metadata:
name: api.route
namespace: default
labels:
app: traefik
spec:
entryPoints:

View File

@@ -0,0 +1,19 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
[api]
[entryPoints]
[entryPoints.footcp]
address = ":8093"
[entryPoints.fooudp]
address = ":8090/udp"
[entryPoints.web]
address = ":8000"
[providers.kubernetesCRD]
labelSelector = "app=traefik"

View File

@@ -0,0 +1,16 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
[api]
insecure = true
[entryPoints]
[entryPoints.web]
address = ":8000"
[providers.kubernetesIngress]
labelSelector = "app=traefik"

View File

@@ -74,6 +74,17 @@ func (s *K8sSuite) TestIngressConfiguration(c *check.C) {
testConfiguration(c, "testdata/rawdata-ingress.json", "8080")
}
func (s *K8sSuite) TestIngressLabelSelector(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingress-label-selector.json", "8080")
}
func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
defer display(c)
@@ -85,6 +96,17 @@ func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
testConfiguration(c, "testdata/rawdata-crd.json", "8000")
}
func (s *K8sSuite) TestCRDLabelSelector(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-crd-label-selector.json", "8000")
}
func testConfiguration(c *check.C, path, apiPort string) {
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
c.Assert(err, checker.IsNil)

View File

@@ -1,5 +1,5 @@
pebble:
image: letsencrypt/pebble:2018-11-02
image: letsencrypt/pebble:v2.3.1
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
ports:
- 14000:14000

View File

@@ -1,5 +1,5 @@
haproxy:
image: haproxy
image: haproxy:2.2
volumes:
- ../haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg

View File

@@ -0,0 +1,65 @@
{
"routers": {
"default-api-route-29f28a463fb5d5ba16d2@kubernetescrd": {
"entryPoints": [
"web"
],
"service": "api@internal",
"rule": "PathPrefix(`/api`)",
"status": "enabled",
"using": [
"web"
]
},
"default-test-route-6b204d94623b3df4370c@kubernetescrd": {
"entryPoints": [
"web"
],
"service": "default-test-route-6b204d94623b3df4370c",
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/bar`)",
"priority": 12,
"tls": {
"options": "default-mytlsoption"
},
"status": "enabled",
"using": [
"web"
]
}
},
"services": {
"api@internal": {
"status": "enabled",
"usedBy": [
"default-api-route-29f28a463fb5d5ba16d2@kubernetescrd"
]
},
"dashboard@internal": {
"status": "enabled"
},
"default-test-route-6b204d94623b3df4370c@kubernetescrd": {
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.3:80"
},
{
"url": "http://10.42.0.4:80"
}
],
"passHostHeader": true
},
"status": "enabled",
"usedBy": [
"default-test-route-6b204d94623b3df4370c@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.3:80": "UP",
"http://10.42.0.4:80": "UP"
}
},
"noop@internal": {
"status": "enabled"
}
}
}

View File

@@ -0,0 +1,106 @@
{
"routers": {
"api@internal": {
"entryPoints": [
"traefik"
],
"service": "api@internal",
"rule": "PathPrefix(`/api`)",
"priority": 2147483646,
"status": "enabled",
"using": [
"traefik"
]
},
"dashboard@internal": {
"entryPoints": [
"traefik"
],
"middlewares": [
"dashboard_redirect@internal",
"dashboard_stripprefix@internal"
],
"service": "dashboard@internal",
"rule": "PathPrefix(`/`)",
"priority": 2147483645,
"status": "enabled",
"using": [
"traefik"
]
},
"test-ingress-default-whoami-test-whoami@kubernetes": {
"entryPoints": [
"web"
],
"service": "default-whoami-http",
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)",
"status": "enabled",
"using": [
"web"
]
}
},
"middlewares": {
"dashboard_redirect@internal": {
"redirectRegex": {
"regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$",
"replacement": "${1}/dashboard/",
"permanent": true
},
"status": "enabled",
"usedBy": [
"dashboard@internal"
]
},
"dashboard_stripprefix@internal": {
"stripPrefix": {
"prefixes": [
"/dashboard/",
"/dashboard"
]
},
"status": "enabled",
"usedBy": [
"dashboard@internal"
]
}
},
"services": {
"api@internal": {
"status": "enabled",
"usedBy": [
"api@internal"
]
},
"dashboard@internal": {
"status": "enabled",
"usedBy": [
"dashboard@internal"
]
},
"default-whoami-http@kubernetes": {
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.2:80"
},
{
"url": "http://10.42.0.7:80"
}
],
"passHostHeader": true
},
"status": "enabled",
"usedBy": [
"test-ingress-default-whoami-test-whoami@kubernetes"
],
"serverStatus": {
"http://10.42.0.2:80": "UP",
"http://10.42.0.7:80": "UP"
}
},
"noop@internal": {
"status": "enabled"
}
}
}

View File

@@ -80,6 +80,7 @@ func doOnStruct(field reflect.Value) error {
}
}
}
return nil
}
@@ -107,7 +108,16 @@ func reset(field reflect.Value, name string) error {
}
case reflect.Slice:
if field.Len() > 0 {
field.Set(reflect.MakeSlice(field.Type(), 0, 0))
switch field.Type().Elem().Kind() {
case reflect.String:
slice := reflect.MakeSlice(field.Type(), field.Len(), field.Len())
for j := 0; j < field.Len(); j++ {
slice.Index(j).SetString(maskShort)
}
field.Set(slice)
default:
field.Set(reflect.MakeSlice(field.Type(), 0, 0))
}
}
case reflect.Interface:
if !field.IsNil() {
@@ -130,7 +140,7 @@ func isExported(f reflect.StructField) bool {
func marshal(anomConfig interface{}, indent bool) ([]byte, error) {
if indent {
return json.MarshalIndent(anomConfig, "", " ")
return json.MarshalIndent(anomConfig, "", " ")
}
return json.Marshal(anomConfig)
}

View File

@@ -1,21 +1,39 @@
package anonymize
import (
"flag"
"io/ioutil"
"os"
"strings"
"testing"
"time"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v2/pkg/config/static"
"github.com/traefik/traefik/v2/pkg/ping"
"github.com/traefik/traefik/v2/pkg/plugins"
"github.com/traefik/traefik/v2/pkg/provider/acme"
"github.com/traefik/traefik/v2/pkg/provider/consulcatalog"
"github.com/traefik/traefik/v2/pkg/provider/docker"
"github.com/traefik/traefik/v2/pkg/provider/ecs"
"github.com/traefik/traefik/v2/pkg/provider/file"
"github.com/traefik/traefik/v2/pkg/provider/http"
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd"
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/ingress"
"github.com/traefik/traefik/v2/pkg/provider/kv"
"github.com/traefik/traefik/v2/pkg/provider/kv/consul"
"github.com/traefik/traefik/v2/pkg/provider/kv/etcd"
"github.com/traefik/traefik/v2/pkg/provider/kv/redis"
"github.com/traefik/traefik/v2/pkg/provider/kv/zk"
"github.com/traefik/traefik/v2/pkg/provider/marathon"
"github.com/traefik/traefik/v2/pkg/provider/rancher"
"github.com/traefik/traefik/v2/pkg/provider/rest"
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
"github.com/traefik/traefik/v2/pkg/tracing/datadog"
"github.com/traefik/traefik/v2/pkg/tracing/elastic"
"github.com/traefik/traefik/v2/pkg/tracing/haystack"
"github.com/traefik/traefik/v2/pkg/tracing/instana"
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
@@ -23,6 +41,8 @@ import (
"github.com/traefik/traefik/v2/pkg/types"
)
var updateExpected = flag.Bool("update_expected", false, "Update expected files in fixtures")
func TestDo_globalConfiguration(t *testing.T) {
config := &static.Configuration{}
@@ -31,39 +51,25 @@ func TestDo_globalConfiguration(t *testing.T) {
SendAnonymousUsage: true,
}
config.AccessLog = &types.AccessLog{
FilePath: "AccessLog FilePath",
Format: "AccessLog Format",
Filters: &types.AccessLogFilters{
StatusCodes: []string{"200", "500"},
RetryAttempts: true,
MinDuration: 10,
config.ServersTransport = &static.ServersTransport{
InsecureSkipVerify: true,
RootCAs: []traefiktls.FileOrContent{"root.ca"},
MaxIdleConnsPerHost: 42,
ForwardingTimeouts: &static.ForwardingTimeouts{
DialTimeout: 42,
ResponseHeaderTimeout: 42,
IdleConnTimeout: 42,
},
Fields: &types.AccessLogFields{
DefaultMode: "drop",
Names: map[string]string{
"RequestHost": "keep",
},
Headers: &types.FieldHeaders{
DefaultMode: "drop",
Names: map[string]string{
"Referer": "keep",
},
},
},
BufferingSize: 4,
}
config.Log = &types.TraefikLog{
Level: "Level",
FilePath: "/foo/path",
Format: "json",
}
config.EntryPoints = static.EntryPoints{
"foo": {
"foobar": {
Address: "foo Address",
Transport: &static.EntryPointsTransport{
LifeCycle: &static.LifeCycle{
RequestAcceptGraceTimeout: ptypes.Duration(111 * time.Second),
GraceTimeOut: ptypes.Duration(111 * time.Second),
},
RespondingTimeouts: &static.RespondingTimeouts{
ReadTimeout: ptypes.Duration(111 * time.Second),
WriteTimeout: ptypes.Duration(111 * time.Second),
@@ -71,38 +77,34 @@ func TestDo_globalConfiguration(t *testing.T) {
},
},
ProxyProtocol: &static.ProxyProtocol{
Insecure: true,
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
},
},
"fii": {
Address: "fii Address",
Transport: &static.EntryPointsTransport{
RespondingTimeouts: &static.RespondingTimeouts{
ReadTimeout: ptypes.Duration(111 * time.Second),
WriteTimeout: ptypes.Duration(111 * time.Second),
IdleTimeout: ptypes.Duration(111 * time.Second),
ForwardedHeaders: &static.ForwardedHeaders{
Insecure: true,
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
},
HTTP: static.HTTPConfig{
Redirections: &static.Redirections{
EntryPoint: &static.RedirectEntryPoint{
To: "foobar",
Scheme: "foobar",
Permanent: true,
Priority: 42,
},
},
Middlewares: []string{"foobar", "foobar"},
TLS: &static.TLSConfig{
Options: "foobar",
CertResolver: "foobar",
Domains: []types.Domain{
{Main: "foobar", SANs: []string{"foobar", "foobar"}},
},
},
},
ProxyProtocol: &static.ProxyProtocol{
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
},
},
}
config.CertificatesResolvers = map[string]static.CertificateResolver{
"default": {
ACME: &acme.Configuration{
Email: "acme Email",
CAServer: "CAServer",
Storage: "Storage",
KeyType: "MyKeyType",
DNSChallenge: &acme.DNSChallenge{Provider: "DNSProvider"},
HTTPChallenge: &acme.HTTPChallenge{
EntryPoint: "MyEntryPoint",
},
TLSChallenge: &acme.TLSChallenge{},
},
},
}
config.Providers = &static.Providers{
ProvidersThrottleDuration: ptypes.Duration(111 * time.Second),
}
@@ -114,22 +116,7 @@ func TestDo_globalConfiguration(t *testing.T) {
ForwardingTimeouts: &static.ForwardingTimeouts{
DialTimeout: ptypes.Duration(111 * time.Second),
ResponseHeaderTimeout: ptypes.Duration(111 * time.Second),
},
}
config.API = &static.API{
Dashboard: true,
DashboardAssets: &assetfs.AssetFS{
Asset: func(path string) ([]byte, error) {
return nil, nil
},
AssetDir: func(path string) ([]string, error) {
return nil, nil
},
AssetInfo: func(path string) (os.FileInfo, error) {
return nil, nil
},
Prefix: "fii",
IdleConnTimeout: ptypes.Duration(111 * time.Second),
},
}
@@ -159,6 +146,33 @@ func TestDo_globalConfiguration(t *testing.T) {
SwarmModeRefreshSeconds: 42,
}
config.Providers.Marathon = &marathon.Provider{
Constraints: `Label("foo", "bar")`,
Trace: true,
Watch: true,
Endpoint: "foobar",
DefaultRule: "PathPrefix(`/`)",
ExposedByDefault: true,
DCOSToken: "foobar",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
DialerTimeout: 42,
ResponseHeaderTimeout: 42,
TLSHandshakeTimeout: 42,
KeepAlive: 42,
ForceTaskHostname: true,
Basic: &marathon.Basic{
HTTPBasicAuthUser: "user",
HTTPBasicPassword: "password",
},
RespectReadinessChecks: true,
}
config.Providers.KubernetesIngress = &ingress.Provider{
Endpoint: "MyEndpoint",
Token: "MyToken",
@@ -167,6 +181,12 @@ func TestDo_globalConfiguration(t *testing.T) {
Namespaces: []string{"a", "b"},
LabelSelector: "myLabelSelector",
IngressClass: "MyIngressClass",
IngressEndpoint: &ingress.EndpointIngress{
IP: "IP",
Hostname: "Hostname",
PublishedService: "PublishedService",
},
ThrottleDuration: ptypes.Duration(111 * time.Second),
}
config.Providers.KubernetesCRD = &crd.Provider{
@@ -177,83 +197,343 @@ func TestDo_globalConfiguration(t *testing.T) {
Namespaces: []string{"a", "b"},
LabelSelector: "myLabelSelector",
IngressClass: "MyIngressClass",
ThrottleDuration: ptypes.Duration(111 * time.Second),
}
// FIXME Test the other providers once they are migrated
config.Providers.Rest = &rest.Provider{
Insecure: true,
}
config.Providers.Rancher = &rancher.Provider{
Constraints: `Label("foo", "bar")`,
Watch: true,
DefaultRule: "PathPrefix(`/`)",
ExposedByDefault: true,
EnableServiceHealthFilter: true,
RefreshSeconds: 42,
IntervalPoll: true,
Prefix: "MyPrefix",
}
config.Providers.ConsulCatalog = &consulcatalog.Provider{
Constraints: `Label("foo", "bar")`,
Endpoint: &consulcatalog.EndpointConfig{
Address: "MyAddress",
Scheme: "MyScheme",
DataCenter: "MyDatacenter",
Token: "MyToken",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
HTTPAuth: &consulcatalog.EndpointHTTPAuthConfig{
Username: "MyUsername",
Password: "MyPassword",
},
EndpointWaitTime: 42,
},
Prefix: "MyPrefix",
RefreshInterval: 42,
RequireConsistent: true,
Stale: true,
Cache: true,
ExposedByDefault: true,
DefaultRule: "PathPrefix(`/`)",
}
config.Providers.Ecs = &ecs.Provider{
Constraints: `Label("foo", "bar")`,
ExposedByDefault: true,
RefreshSeconds: 42,
DefaultRule: "PathPrefix(`/`)",
Clusters: []string{"Cluster1", "Cluster2"},
AutoDiscoverClusters: true,
Region: "Awsregion",
AccessKeyID: "AwsAccessKeyID",
SecretAccessKey: "AwsSecretAccessKey",
}
config.Providers.Consul = &consul.Provider{
Provider: kv.Provider{
RootKey: "RootKey",
Endpoints: nil,
Username: "username",
Password: "password",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
},
}
config.Providers.Etcd = &etcd.Provider{
Provider: kv.Provider{
RootKey: "RootKey",
Endpoints: nil,
Username: "username",
Password: "password",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
},
}
config.Providers.ZooKeeper = &zk.Provider{
Provider: kv.Provider{
RootKey: "RootKey",
Endpoints: nil,
Username: "username",
Password: "password",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
},
}
config.Providers.Redis = &redis.Provider{
Provider: kv.Provider{
RootKey: "RootKey",
Endpoints: nil,
Username: "username",
Password: "password",
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
},
}
config.Providers.HTTP = &http.Provider{
Endpoint: "Myenpoint",
PollInterval: 42,
PollTimeout: 42,
TLS: &types.ClientTLS{
CA: "myCa",
CAOptional: true,
Cert: "mycert.pem",
Key: "mycert.key",
InsecureSkipVerify: true,
},
}
config.API = &static.API{
Insecure: true,
Dashboard: true,
Debug: true,
DashboardAssets: &assetfs.AssetFS{
Asset: func(path string) ([]byte, error) {
return nil, nil
},
AssetDir: func(path string) ([]string, error) {
return nil, nil
},
AssetInfo: func(path string) (os.FileInfo, error) {
return nil, nil
},
Prefix: "fii",
},
}
config.Metrics = &types.Metrics{
Prometheus: &types.Prometheus{
Buckets: []float64{0.1, 0.3, 1.2, 5},
Buckets: []float64{0.1, 0.3, 1.2, 5},
AddEntryPointsLabels: true,
AddServicesLabels: true,
EntryPoint: "MyEntryPoint",
ManualRouting: true,
},
Datadog: &types.Datadog{
Address: "localhost:8181",
PushInterval: 12,
Address: "localhost:8181",
PushInterval: 42,
AddEntryPointsLabels: true,
AddServicesLabels: true,
},
StatsD: &types.Statsd{
Address: "localhost:8182",
PushInterval: 42,
Address: "localhost:8182",
PushInterval: 42,
AddEntryPointsLabels: true,
AddServicesLabels: true,
Prefix: "MyPrefix",
},
InfluxDB: &types.InfluxDB{
Address: "localhost:8183",
Protocol: "http",
PushInterval: 22,
Database: "myDB",
RetentionPolicy: "12",
Username: "a",
Password: "aaaa",
Address: "localhost:8183",
Protocol: "http",
PushInterval: 42,
Database: "myDB",
RetentionPolicy: "12",
Username: "a",
Password: "aaaa",
AddEntryPointsLabels: true,
AddServicesLabels: true,
},
}
config.Ping = &ping.Handler{}
config.Ping = &ping.Handler{
EntryPoint: "MyEntryPoint",
ManualRouting: true,
TerminatingStatusCode: 42,
}
config.Log = &types.TraefikLog{
Level: "Level",
FilePath: "/foo/path",
Format: "json",
}
config.AccessLog = &types.AccessLog{
FilePath: "AccessLog FilePath",
Format: "AccessLog Format",
Filters: &types.AccessLogFilters{
StatusCodes: []string{"200", "500"},
RetryAttempts: true,
MinDuration: 42,
},
Fields: &types.AccessLogFields{
DefaultMode: "drop",
Names: map[string]string{
"RequestHost": "keep",
},
Headers: &types.FieldHeaders{
DefaultMode: "drop",
Names: map[string]string{
"Referer": "keep",
},
},
},
BufferingSize: 42,
}
config.Tracing = &static.Tracing{
ServiceName: "myServiceName",
SpanNameLimit: 3,
SpanNameLimit: 42,
Jaeger: &jaeger.Config{
SamplingServerURL: "aaa",
SamplingType: "bbb",
SamplingParam: 43,
LocalAgentHostPort: "ccc",
SamplingServerURL: "foobar",
SamplingType: "foobar",
SamplingParam: 42,
LocalAgentHostPort: "foobar",
Gen128Bit: true,
Propagation: "ddd",
TraceContextHeaderName: "eee",
Propagation: "foobar",
TraceContextHeaderName: "foobar",
Collector: &jaeger.Collector{
Endpoint: "foobar",
User: "foobar",
Password: "foobar",
},
DisableAttemptReconnecting: true,
},
Zipkin: &zipkin.Config{
HTTPEndpoint: "fff",
HTTPEndpoint: "foobar",
SameSpan: true,
ID128Bit: true,
SampleRate: 53,
SampleRate: 42,
},
Datadog: &datadog.Config{
LocalAgentHostPort: "ggg",
GlobalTag: "eee",
Debug: true,
PrioritySampling: true,
LocalAgentHostPort: "foobar",
GlobalTag: "foobar",
Debug: true,
PrioritySampling: true,
TraceIDHeaderName: "foobar",
ParentIDHeaderName: "foobar",
SamplingPriorityHeaderName: "foobar",
BagagePrefixHeaderName: "foobar",
},
Instana: &instana.Config{
LocalAgentHost: "fff",
LocalAgentPort: 32,
LogLevel: "ggg",
LocalAgentHost: "foobar",
LocalAgentPort: 4242,
LogLevel: "foobar",
},
Haystack: &haystack.Config{
LocalAgentHost: "fff",
LocalAgentPort: 32,
GlobalTag: "eee",
TraceIDHeaderName: "fff",
ParentIDHeaderName: "ggg",
SpanIDHeaderName: "hhh",
BaggagePrefixHeaderName: "iii",
LocalAgentHost: "foobar",
LocalAgentPort: 42,
GlobalTag: "foobar",
TraceIDHeaderName: "foobar",
ParentIDHeaderName: "foobar",
SpanIDHeaderName: "foobar",
BaggagePrefixHeaderName: "foobar",
},
Elastic: &elastic.Config{
ServerURL: "foobar",
SecretToken: "foobar",
ServiceEnvironment: "foobar",
},
}
config.HostResolver = &types.HostResolverConfig{
CnameFlattening: true,
ResolvConfig: "aaa",
ResolvDepth: 3,
ResolvConfig: "foobar",
ResolvDepth: 42,
}
cleanJSON, err := Do(config, true)
if err != nil {
t.Fatal(err, cleanJSON)
config.CertificatesResolvers = map[string]static.CertificateResolver{
"CertificateResolver0": {
ACME: &acme.Configuration{
Email: "acme Email",
CAServer: "CAServer",
PreferredChain: "foobar",
Storage: "Storage",
KeyType: "MyKeyType",
DNSChallenge: &acme.DNSChallenge{
Provider: "DNSProvider",
DelayBeforeCheck: 42,
Resolvers: []string{"resolver1", "resolver2"},
DisablePropagationCheck: true,
},
HTTPChallenge: &acme.HTTPChallenge{
EntryPoint: "MyEntryPoint",
},
TLSChallenge: &acme.TLSChallenge{},
},
},
}
config.Pilot = &static.Pilot{
Token: "token",
}
config.Experimental = &static.Experimental{
Plugins: map[string]plugins.Descriptor{
"Descriptor0": {
ModuleName: "foobar",
Version: "foobar",
},
"Descriptor1": {
ModuleName: "foobar",
Version: "foobar",
},
},
DevPlugin: &plugins.DevPlugin{
GoPath: "foobar",
ModuleName: "foobar",
},
}
expectedConfiguration, err := ioutil.ReadFile("./testdata/anonymized-static-config.json")
require.NoError(t, err)
cleanJSON, err := Do(config, true)
require.NoError(t, err)
if *updateExpected {
require.NoError(t, ioutil.WriteFile("testdata/anonymized-static-config.json", []byte(cleanJSON), 0666))
}
expected := strings.TrimSuffix(string(expectedConfiguration), "\n")
assert.Equal(t, expected, cleanJSON)
}

View File

@@ -1,185 +1,23 @@
package anonymize
import (
"io/ioutil"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_doOnJSON(t *testing.T) {
baseConfiguration := `
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"Level": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "foo@bar.com",
"Domains": [
{
"Main": "foo@bar.com",
"SANs": null
},
{
"Main": "foo@bar.com",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"Options": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"KubernetesIngress": null,
"KubernetesCRD": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}
`
expectedConfiguration := `
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"Level": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Domains": [
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
},
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"Options": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"KubernetesIngress": null,
"KubernetesCRD": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}
`
anomConfiguration := doOnJSON(baseConfiguration)
baseConfiguration, err := ioutil.ReadFile("./testdata/example.json")
require.NoError(t, err)
if anomConfiguration != expectedConfiguration {
t.Errorf("Got %s, want %s.", anomConfiguration, expectedConfiguration)
}
anomConfiguration := doOnJSON(string(baseConfiguration))
expectedConfiguration, err := ioutil.ReadFile("./testdata/expected.json")
require.NoError(t, err)
assert.JSONEq(t, string(expectedConfiguration), anomConfiguration)
}
func Test_doOnJSON_simple(t *testing.T) {

View File

@@ -20,6 +20,8 @@ type Tomate struct {
type Carotte struct {
Name string
Value int
List []string
EList []string `export:"true"`
Courgette Courgette
ECourgette Courgette `export:"true"`
Pourgette *Courgette
@@ -44,9 +46,13 @@ func Test_doOnStruct(t *testing.T) {
base: &Carotte{
Name: "koko",
Value: 666,
List: []string{"test"},
EList: []string{"test"},
},
expected: &Carotte{
Name: "xxxx",
Name: "xxxx",
List: []string{"xxxx"},
EList: []string{"test"},
},
},
{

View File

@@ -0,0 +1,454 @@
{
"global": {
"checkNewVersion": true,
"sendAnonymousUsage": true
},
"serversTransport": {
"insecureSkipVerify": true,
"rootCAs": [
"xxxx",
"xxxx",
"xxxx"
],
"maxIdleConnsPerHost": 111,
"forwardingTimeouts": {
"dialTimeout": 111000000000,
"responseHeaderTimeout": 111000000000,
"idleConnTimeout": 111000000000
}
},
"entryPoints": {
"foobar": {
"address": "xxxx",
"transport": {
"lifeCycle": {
"requestAcceptGraceTimeout": 111000000000,
"graceTimeOut": 111000000000
},
"respondingTimeouts": {
"readTimeout": 111000000000,
"writeTimeout": 111000000000,
"idleTimeout": 111000000000
}
},
"proxyProtocol": {
"insecure": true,
"trustedIPs": [
"xxxx",
"xxxx"
]
},
"forwardedHeaders": {
"insecure": true,
"trustedIPs": [
"xxxx",
"xxxx"
]
},
"http": {
"redirections": {
"entryPoint": {
"to": "foobar",
"scheme": "foobar",
"permanent": true,
"priority": 42
}
},
"middlewares": [
"foobar",
"foobar"
],
"tls": {
"options": "foobar",
"certResolver": "foobar",
"domains": [
{
"main": "xxxx",
"sans": [
"xxxx",
"xxxx"
]
}
]
}
}
}
},
"providers": {
"providersThrottleDuration": 111000000000,
"docker": {
"constraints": "Label(\"foo\", \"bar\")",
"watch": true,
"endpoint": "xxxx",
"defaultRule": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
},
"exposedByDefault": true,
"useBindPortIP": true,
"swarmMode": true,
"network": "MyNetwork",
"swarmModeRefreshSeconds": 42
},
"file": {
"directory": "file Directory",
"watch": true,
"filename": "file Filename",
"debugLogGeneratedTemplate": true
},
"marathon": {
"constraints": "Label(\"foo\", \"bar\")",
"trace": true,
"watch": true,
"endpoint": "xxxx",
"defaultRule": "xxxx",
"exposedByDefault": true,
"dcosToken": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
},
"dialerTimeout": 42,
"responseHeaderTimeout": 42,
"tlsHandshakeTimeout": 42,
"keepAlive": 42,
"forceTaskHostname": true,
"basic": {
"httpBasicAuthUser": "xxxx",
"httpBasicPassword": "xxxx"
},
"respectReadinessChecks": true
},
"kubernetesIngress": {
"endpoint": "xxxx",
"token": "xxxx",
"certAuthFilePath": "xxxx",
"disablePassHostHeaders": true,
"namespaces": [
"a",
"b"
],
"labelSelector": "myLabelSelector",
"ingressClass": "MyIngressClass",
"ingressEndpoint": {
"ip": "xxxx",
"hostname": "xxxx",
"publishedService": "xxxx"
},
"throttleDuration": 111000000000
},
"kubernetesCRD": {
"endpoint": "xxxx",
"token": "xxxx",
"certAuthFilePath": "xxxx",
"disablePassHostHeaders": true,
"namespaces": [
"a",
"b"
],
"labelSelector": "myLabelSelector",
"ingressClass": "MyIngressClass",
"throttleDuration": 111000000000
},
"rest": {
"insecure": true
},
"rancher": {
"constraints": "Label(\"foo\", \"bar\")",
"watch": true,
"defaultRule": "xxxx",
"exposedByDefault": true,
"enableServiceHealthFilter": true,
"refreshSeconds": 42,
"intervalPoll": true,
"prefix": "xxxx"
},
"consulCatalog": {
"constraints": "Label(\"foo\", \"bar\")",
"endpoint": {
"address": "xxxx",
"scheme": "xxxx",
"datacenter": "xxxx",
"token": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
},
"httpAuth": {
"username": "xxxx",
"password": "xxxx"
},
"endpointWaitTime": 42
},
"prefix": "MyPrefix",
"refreshInterval": 42,
"requireConsistent": true,
"stale": true,
"cache": true,
"exposedByDefault": true,
"defaultRule": "xxxx"
},
"ecs": {
"constraints": "Label(\"foo\", \"bar\")",
"exposedByDefault": true,
"refreshSeconds": 42,
"defaultRule": "xxxx",
"clusters": [
"Cluster1",
"Cluster2"
],
"autoDiscoverClusters": true,
"region": "Awsregion",
"accessKeyID": "xxxx",
"secretAccessKey": "xxxx"
},
"consul": {
"rootKey": "RootKey",
"username": "xxxx",
"password": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
}
},
"etcd": {
"rootKey": "RootKey",
"username": "xxxx",
"password": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
}
},
"zooKeeper": {
"rootKey": "RootKey",
"username": "xxxx",
"password": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
}
},
"redis": {
"rootKey": "RootKey",
"username": "xxxx",
"password": "xxxx",
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
}
},
"http": {
"endpoint": "xxxx",
"pollInterval": 42,
"pollTimeout": 42,
"tls": {
"ca": "xxxx",
"caOptional": true,
"cert": "xxxx",
"key": "xxxx",
"insecureSkipVerify": true
}
}
},
"api": {
"insecure": true,
"dashboard": true,
"debug": true
},
"metrics": {
"prometheus": {
"buckets": [
0.1,
0.3,
1.2,
5
],
"addEntryPointsLabels": true,
"addServicesLabels": true,
"entryPoint": "MyEntryPoint",
"manualRouting": true
},
"datadog": {
"address": "xxxx",
"pushInterval": 42,
"addEntryPointsLabels": true,
"addServicesLabels": true
},
"statsD": {
"address": "xxxx",
"pushInterval": 42,
"addEntryPointsLabels": true,
"addServicesLabels": true,
"prefix": "MyPrefix"
},
"influxDB": {
"address": "xxxx",
"protocol": "xxxx",
"pushInterval": 42,
"database": "myDB",
"retentionPolicy": "12",
"username": "xxxx",
"password": "xxxx",
"addEntryPointsLabels": true,
"addServicesLabels": true
}
},
"ping": {
"entryPoint": "MyEntryPoint",
"manualRouting": true,
"terminatingStatusCode": 42
},
"log": {
"level": "Level",
"filePath": "xxxx",
"format": "json"
},
"accessLog": {
"filePath": "xxxx",
"format": "AccessLog Format",
"filters": {
"statusCodes": [
"200",
"500"
],
"retryAttempts": true,
"minDuration": 42
},
"fields": {
"defaultMode": "drop",
"names": {
"RequestHost": "keep"
},
"headers": {
"defaultMode": "drop",
"names": {
"Referer": "keep"
}
}
},
"bufferingSize": 42
},
"tracing": {
"serviceName": "myServiceName",
"spanNameLimit": 42,
"jaeger": {
"samplingServerURL": "xxxx",
"samplingType": "foobar",
"samplingParam": 42,
"localAgentHostPort": "xxxx",
"gen128Bit": true,
"propagation": "foobar",
"traceContextHeaderName": "foobar",
"collector": {
"endpoint": "xxxx",
"user": "xxxx",
"password": "xxxx"
},
"disableAttemptReconnecting": true
},
"zipkin": {
"httpEndpoint": "xxxx",
"sameSpan": true,
"id128Bit": true,
"sampleRate": 42
},
"datadog": {
"localAgentHostPort": "xxxx",
"globalTag": "foobar",
"debug": true,
"prioritySampling": true,
"traceIDHeaderName": "foobar",
"parentIDHeaderName": "foobar",
"samplingPriorityHeaderName": "foobar",
"bagagePrefixHeaderName": "foobar"
},
"instana": {
"localAgentHost": "xxxx",
"logLevel": "foobar"
},
"haystack": {
"localAgentHost": "xxxx",
"globalTag": "foobar",
"traceIDHeaderName": "foobar",
"parentIDHeaderName": "foobar",
"spanIDHeaderName": "foobar",
"baggagePrefixHeaderName": "foobar"
},
"elastic": {
"serverURL": "xxxx",
"secretToken": "xxxx",
"serviceEnvironment": "foobar"
}
},
"hostResolver": {
"cnameFlattening": true,
"resolvConfig": "foobar",
"resolvDepth": 42
},
"certificatesResolvers": {
"CertificateResolver0": {
"acme": {
"email": "xxxx",
"caServer": "xxxx",
"preferredChain": "foobar",
"storage": "Storage",
"keyType": "MyKeyType",
"dnsChallenge": {
"provider": "DNSProvider",
"delayBeforeCheck": 42,
"resolvers": [
"xxxx",
"xxxx"
],
"disablePropagationCheck": true
},
"httpChallenge": {
"entryPoint": "MyEntryPoint"
},
"tlsChallenge": {}
}
}
},
"pilot": {
"token": "xxxx"
},
"experimental": {
"plugins": {
"Descriptor0": {
"moduleName": "foobar",
"version": "foobar"
},
"Descriptor1": {
"moduleName": "foobar",
"version": "foobar"
}
},
"devPlugin": {
"goPath": "foobar",
"moduleName": "foobar"
}
}
}

82
pkg/anonymize/testdata/example.json vendored Normal file
View File

@@ -0,0 +1,82 @@
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"Level": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "foo@bar.com",
"Domains": [
{
"Main": "foo@bar.com",
"SANs": null
},
{
"Main": "foo@bar.com",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"Options": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"KubernetesIngress": null,
"KubernetesCRD": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}

82
pkg/anonymize/testdata/expected.json vendored Normal file
View File

@@ -0,0 +1,82 @@
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"Level": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Domains": [
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
},
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"Options": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"KubernetesIngress": null,
"KubernetesCRD": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}

View File

@@ -11,10 +11,10 @@ import (
// EntryPoint holds the entry point configuration.
type EntryPoint struct {
Address string `description:"Entry point address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty"`
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty"`
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty"`
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty"`
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty" export:"true"`
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty" export:"true"`
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"`
}
// GetAddress strips any potential protocol part of the address field of the
@@ -49,22 +49,22 @@ func (ep *EntryPoint) SetDefaults() {
// HTTPConfig is the HTTP configuration of an entry point.
type HTTPConfig struct {
Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty"`
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty" export:"true"`
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// Redirections is a set of redirection for an entry point.
type Redirections struct {
EntryPoint *RedirectEntryPoint `description:"Set of redirection for an entry point." json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"`
EntryPoint *RedirectEntryPoint `description:"Set of redirection for an entry point." json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"`
}
// RedirectEntryPoint is the definition of an entry point redirection.
type RedirectEntryPoint struct {
To string `description:"Targeted entry point of the redirection." json:"to,omitempty" toml:"to,omitempty" yaml:"to,omitempty"`
Scheme string `description:"Scheme used for the redirection." json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"`
Permanent bool `description:"Applies a permanent redirection." json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty"`
Priority int `description:"Priority of the generated router." json:"priority,omitempty" toml:"priority,omitempty" yaml:"priority,omitempty"`
To string `description:"Targeted entry point of the redirection." json:"to,omitempty" toml:"to,omitempty" yaml:"to,omitempty" export:"true"`
Scheme string `description:"Scheme used for the redirection." json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"`
Permanent bool `description:"Applies a permanent redirection." json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"`
Priority int `description:"Priority of the generated router." json:"priority,omitempty" toml:"priority,omitempty" yaml:"priority,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@@ -76,9 +76,9 @@ func (r *RedirectEntryPoint) SetDefaults() {
// TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point.
type TLSConfig struct {
Options string `description:"Default TLS options for the routers linked to the entry point." json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty"`
CertResolver string `description:"Default certificate resolver for the routers linked to the entry point." json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty"`
Domains []types.Domain `description:"Default TLS domains for the routers linked to the entry point." json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty"`
Options string `description:"Default TLS options for the routers linked to the entry point." json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"`
CertResolver string `description:"Default certificate resolver for the routers linked to the entry point." json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty" export:"true"`
Domains []types.Domain `description:"Default TLS domains for the routers linked to the entry point." json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty" export:"true"`
}
// ForwardedHeaders Trust client forwarding headers.

View File

@@ -4,6 +4,6 @@ import "github.com/traefik/traefik/v2/pkg/plugins"
// Experimental experimental Traefik features.
type Experimental struct {
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty"`
DevPlugin *plugins.DevPlugin `description:"Dev plugin configuration." json:"devPlugin,omitempty" toml:"devPlugin,omitempty" yaml:"devPlugin,omitempty"`
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"`
DevPlugin *plugins.DevPlugin `description:"Dev plugin configuration." json:"devPlugin,omitempty" toml:"devPlugin,omitempty" yaml:"devPlugin,omitempty" export:"true"`
}

View File

@@ -72,9 +72,9 @@ type Configuration struct {
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
Pilot *Pilot `description:"Traefik Pilot configuration." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty"`
Pilot *Pilot `description:"Traefik Pilot configuration." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty" export:"true"`
Experimental *Experimental `description:"experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty"`
Experimental *Experimental `description:"experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" export:"true"`
}
// CertificateResolver contains the configuration for the different types of certificates resolver.
@@ -176,14 +176,14 @@ type Providers struct {
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
ConsulCatalog *consulcatalog.Provider `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty"`
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty"`
ConsulCatalog *consulcatalog.Provider `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
HTTP *http.Provider `description:"Enable HTTP backend with default settings." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *http.Provider `description:"Enable HTTP backend with default settings." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.

View File

@@ -137,7 +137,7 @@ func RotateFile() error {
}
if err := OpenFile(logFilePath); err != nil {
return fmt.Errorf("error opening log file: %s", err)
return fmt.Errorf("error opening log file: %w", err)
}
return nil

View File

@@ -2,6 +2,7 @@ package metrics
import (
"context"
"errors"
"net/http"
"sort"
"strings"
@@ -74,12 +75,15 @@ func RegisterPrometheus(ctx context.Context, config *types.Prometheus) Registry
standardRegistry := initStandardRegistry(config)
if err := promRegistry.Register(stdprometheus.NewProcessCollector(stdprometheus.ProcessCollectorOpts{})); err != nil {
if _, ok := err.(stdprometheus.AlreadyRegisteredError); !ok {
var arErr stdprometheus.AlreadyRegisteredError
if !errors.As(err, &arErr) {
log.FromContext(ctx).Warn("ProcessCollector is already registered")
}
}
if err := promRegistry.Register(stdprometheus.NewGoCollector()); err != nil {
if _, ok := err.(stdprometheus.AlreadyRegisteredError); !ok {
var arErr stdprometheus.AlreadyRegisteredError
if !errors.As(err, &arErr) {
log.FromContext(ctx).Warn("GoCollector is already registered")
}
}
@@ -212,15 +216,21 @@ func initStandardRegistry(config *types.Prometheus) Registry {
}
func registerPromState(ctx context.Context) bool {
if err := promRegistry.Register(promState); err != nil {
logger := log.FromContext(ctx)
if _, ok := err.(stdprometheus.AlreadyRegisteredError); !ok {
logger.Errorf("Unable to register Traefik to Prometheus: %v", err)
return false
}
logger.Debug("Prometheus collector already registered.")
err := promRegistry.Register(promState)
if err == nil {
return true
}
return true
logger := log.FromContext(ctx)
var arErr stdprometheus.AlreadyRegisteredError
if errors.As(err, &arErr) {
logger.Debug("Prometheus collector already registered.")
return true
}
logger.Errorf("Unable to register Traefik to Prometheus: %v", err)
return false
}
// OnConfigurationUpdate receives the current configuration from Traefik.

View File

@@ -60,33 +60,31 @@ func TestCommonLogFormatter_Format(t *testing.T) {
expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms
`,
},
/*
{
name: "all data with local time",
data: map[string]interface{}{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
ClientUsername: "Client",
RequestMethod: http.MethodGet,
RequestPath: "/foo",
RequestProtocol: "http",
OriginStatus: 123,
OriginContentSize: 132,
RequestRefererHeader: "referer",
RequestUserAgentHeader: "agent",
RequestCount: nil,
RouterName: "foo",
ServiceURL: "http://10.0.0.2/toto",
},
expectedLog: `10.0.0.1 - Client [10/Nov/2009:14:00:00 -0900] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms
`,
},
*/
{
name: "all data with local time",
data: map[string]interface{}{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
ClientUsername: "Client",
RequestMethod: http.MethodGet,
RequestPath: "/foo",
RequestProtocol: "http",
OriginStatus: 123,
OriginContentSize: 132,
RequestRefererHeader: "referer",
RequestUserAgentHeader: "agent",
RequestCount: nil,
RouterName: "foo",
ServiceURL: "http://10.0.0.2/toto",
},
expectedLog: `10.0.0.1 - Client [10/Nov/2009:14:00:00 -0900] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms
`,
},
}
// Set timezone to Alaska to have a constant behavior
os.Setenv("TZ", "US/Alaska")
// Set timezone to Etc/GMT+9 to have a constant behavior
os.Setenv("TZ", "Etc/GMT+9")
for _, test := range testCases {
test := test

View File

@@ -2,6 +2,7 @@ package auth
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
@@ -124,7 +125,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
redirectURL, err := forwardResponse.Location()
if err != nil {
if err != http.ErrNoLocation {
if !errors.Is(err, http.ErrNoLocation) {
logMessage := fmt.Sprintf("Error reading response location header %s. Cause: %s", fa.address, err)
logger.Debug(logMessage)
tracing.SetErrorWithEvent(req, logMessage)

View File

@@ -54,5 +54,6 @@ func recoverFunc(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
// https://github.com/golang/go/blob/a0d6420d8be2ae7164797051ec74fa2a2df466a1/src/net/http/server.go#L1761-L1775
// https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284
func shouldLogPanic(panicValue interface{}) bool {
//nolint:errorlint // false-positive because panicValue is an interface.
return panicValue != nil && panicValue != http.ErrAbortHandler
}

View File

@@ -1,9 +1,6 @@
package redirect
import (
"bytes"
"html/template"
"io"
"net/http"
"net/url"
"regexp"
@@ -47,24 +44,17 @@ func (r *redirect) GetTracingInformation() (string, ext.SpanKindEnum) {
func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
oldURL := rawURL(req)
// If the Regexp doesn't match, skip to the next handler
// If the Regexp doesn't match, skip to the next handler.
if !r.regex.MatchString(oldURL) {
r.next.ServeHTTP(rw, req)
return
}
// apply a rewrite regexp to the URL
// Apply a rewrite regexp to the URL.
newURL := r.regex.ReplaceAllString(oldURL, r.replacement)
// replace any variables that may be in there
rewrittenURL := &bytes.Buffer{}
if err := applyString(newURL, rewrittenURL, req); err != nil {
r.errHandler.ServeHTTP(rw, req, err)
return
}
// parse the rewritten URL and replace request URL with it
parsedURL, err := url.Parse(rewrittenURL.String())
// Parse the rewritten URL and replace request URL with it.
parsedURL, err := url.Parse(newURL)
if err != nil {
r.errHandler.ServeHTTP(rw, req, err)
return
@@ -78,7 +68,7 @@ func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
req.URL = parsedURL
// make sure the request URI corresponds the rewritten URL
// Make sure the request URI corresponds the rewritten URL.
req.RequestURI = req.URL.RequestURI()
r.next.ServeHTTP(rw, req)
}
@@ -138,14 +128,3 @@ func rawURL(req *http.Request) string {
return strings.Join([]string{scheme, "://", host, port, uri}, "")
}
func applyString(in string, out io.Writer, req *http.Request) error {
t, err := template.New("t").Parse(in)
if err != nil {
return err
}
data := struct{ Request *http.Request }{Request: req}
return t.Execute(out, data)
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/testhelpers"
)
func TestRedirectRegexHandler(t *testing.T) {
@@ -35,16 +34,6 @@ func TestRedirectRegexHandler(t *testing.T) {
expectedURL: "https://foobar.com:443",
expectedStatus: http.StatusFound,
},
{
desc: "use request header",
config: dynamic.RedirectRegex{
Regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
Replacement: `https://${1}{{ .Request.Header.Get "X-Foo" }}$2:443$4`,
},
url: "http://foo.com:80",
expectedURL: "https://foobar.com:443",
expectedStatus: http.StatusFound,
},
{
desc: "URL doesn't match regex",
config: dynamic.RedirectRegex{
@@ -186,7 +175,7 @@ func TestRedirectRegexHandler(t *testing.T) {
method = test.method
}
req := testhelpers.MustNewRequest(method, test.url, nil)
req := httptest.NewRequest(method, test.url, nil)
if test.secured {
req.TLS = &tls.ConnectionState{}
}

View File

@@ -9,8 +9,8 @@ import (
// Handler expose ping routes.
type Handler struct {
EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"`
ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty"`
TerminatingStatusCode int `description:"Terminating status code" json:"terminatingStatusCode,omitempty" toml:"terminatingStatusCode,omitempty" yaml:"terminatingStatusCode,omitempty"`
ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty" export:"true"`
TerminatingStatusCode int `description:"Terminating status code" json:"terminatingStatusCode,omitempty" toml:"terminatingStatusCode,omitempty" yaml:"terminatingStatusCode,omitempty" export:"true"`
terminating bool
}

View File

@@ -3,19 +3,19 @@ package plugins
// Descriptor The static part of a plugin configuration (prod).
type Descriptor struct {
// ModuleName (required)
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty"`
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
// Version (required)
Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty"`
Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"`
}
// DevPlugin The static part of a plugin configuration (only for dev).
type DevPlugin struct {
// GoPath plugin's GOPATH. (required)
GoPath string `description:"plugin's GOPATH." json:"goPath,omitempty" toml:"goPath,omitempty" yaml:"goPath,omitempty"`
GoPath string `description:"plugin's GOPATH." json:"goPath,omitempty" toml:"goPath,omitempty" yaml:"goPath,omitempty" export:"true"`
// ModuleName (required)
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty"`
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
}
// Manifest The plugin manifest.

View File

@@ -35,12 +35,12 @@ var oscpMustStaple = false
type Configuration struct {
Email string `description:"Email address used for registration." json:"email,omitempty" toml:"email,omitempty" yaml:"email,omitempty"`
CAServer string `description:"CA server to use." json:"caServer,omitempty" toml:"caServer,omitempty" yaml:"caServer,omitempty"`
PreferredChain string `description:"Preferred chain to use." json:"preferredChain,omitempty" toml:"preferredChain,omitempty" yaml:"preferredChain,omitempty"`
Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty"`
KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty"`
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
PreferredChain string `description:"Preferred chain to use." json:"preferredChain,omitempty" toml:"preferredChain,omitempty" yaml:"preferredChain,omitempty" export:"true"`
Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty" export:"true"`
KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty" export:"true"`
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
@@ -65,15 +65,15 @@ type Certificate struct {
// DNSChallenge contains DNS challenge Configuration.
type DNSChallenge struct {
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty"`
DelayBeforeCheck ptypes.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty"`
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty" export:"true"`
DelayBeforeCheck ptypes.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty" export:"true"`
Resolvers []string `description:"Use following DNS servers to resolve the FQDN authority." json:"resolvers,omitempty" toml:"resolvers,omitempty" yaml:"resolvers,omitempty"`
DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty"`
DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty" export:"true"`
}
// HTTPChallenge contains HTTP challenge Configuration.
type HTTPChallenge struct {
EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"`
EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"`
}
// TLSChallenge contains TLS challenge Configuration.

View File

@@ -55,10 +55,10 @@ type Provider struct {
// EndpointConfig holds configurations of the endpoint.
type EndpointConfig struct {
Address string `description:"The address of the Consul server" json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" export:"true"`
Scheme string `description:"The URI scheme for the Consul server" json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"`
DataCenter string `description:"Data center to use. If not provided, the default agent data center is used" json:"datacenter,omitempty" toml:"datacenter,omitempty" yaml:"datacenter,omitempty" export:"true"`
Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" export:"true"`
Address string `description:"The address of the Consul server" json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
Scheme string `description:"The URI scheme for the Consul server" json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"`
DataCenter string `description:"Data center to use. If not provided, the default agent data center is used" json:"datacenter,omitempty" toml:"datacenter,omitempty" yaml:"datacenter,omitempty"`
Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
HTTPAuth *EndpointHTTPAuthConfig `description:"Auth info to use for http access" json:"httpAuth,omitempty" toml:"httpAuth,omitempty" yaml:"httpAuth,omitempty" export:"true"`
EndpointWaitTime ptypes.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"`
@@ -71,8 +71,8 @@ func (c *EndpointConfig) SetDefaults() {
// EndpointHTTPAuthConfig holds configurations of the authentication.
type EndpointHTTPAuthConfig struct {
Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"`
Password string `description:"Basic Auth password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"`
Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"`
Password string `description:"Basic Auth password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"`
}
// SetDefaults sets the default values.
@@ -108,27 +108,28 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
p.client, err = createClient(p.Endpoint)
if err != nil {
return fmt.Errorf("error create consul client, %w", err)
return fmt.Errorf("unable to create consul client: %w", err)
}
// get configuration at the provider's startup.
err = p.loadConfiguration(routineCtx, configurationChan)
if err != nil {
return fmt.Errorf("failed to get consul catalog data: %w", err)
}
// Periodic refreshes.
ticker := time.NewTicker(time.Duration(p.RefreshInterval))
defer ticker.Stop()
for {
select {
case <-ticker.C:
data, err := p.getConsulServicesData(routineCtx)
err = p.loadConfiguration(routineCtx, configurationChan)
if err != nil {
logger.Errorf("error get consul catalog data, %v", err)
return err
return fmt.Errorf("failed to refresh consul catalog data: %w", err)
}
configuration := p.buildConfiguration(routineCtx, data)
configurationChan <- dynamic.Message{
ProviderName: "consulcatalog",
Configuration: configuration,
}
case <-routineCtx.Done():
ticker.Stop()
return nil
}
}
@@ -147,6 +148,20 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
func (p *Provider) loadConfiguration(ctx context.Context, configurationChan chan<- dynamic.Message) error {
data, err := p.getConsulServicesData(ctx)
if err != nil {
return err
}
configurationChan <- dynamic.Message{
ProviderName: "consulcatalog",
Configuration: p.buildConfiguration(ctx, data),
}
return nil
}
func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error) {
consulServiceNames, err := p.fetchServices(ctx)
if err != nil {
@@ -155,17 +170,22 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
var data []itemData
for _, name := range consulServiceNames {
consulServices, healthServices, err := p.fetchService(ctx, name)
consulServices, statuses, err := p.fetchService(ctx, name)
if err != nil {
return nil, err
}
for i, consulService := range consulServices {
for _, consulService := range consulServices {
address := consulService.ServiceAddress
if address == "" {
address = consulService.Address
}
status, exists := statuses[consulService.ID+consulService.ServiceID]
if !exists {
status = api.HealthAny
}
item := itemData{
ID: consulService.ServiceID,
Node: consulService.Node,
@@ -174,7 +194,7 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
Port: strconv.Itoa(consulService.ServicePort),
Labels: tagsToNeutralLabels(consulService.ServiceTags, p.Prefix),
Tags: consulService.ServiceTags,
Status: healthServices[i].Checks.AggregatedStatus(),
Status: status,
}
extraConf, err := p.getConfiguration(item)
@@ -190,13 +210,14 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
return data, nil
}
func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.CatalogService, []*api.ServiceEntry, error) {
func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.CatalogService, map[string]string, error) {
var tagFilter string
if !p.ExposedByDefault {
tagFilter = p.Prefix + ".enable=true"
}
opts := &api.QueryOptions{AllowStale: p.Stale, RequireConsistent: p.RequireConsistent, UseCache: p.Cache}
opts = opts.WithContext(ctx)
consulServices, _, err := p.client.Catalog().Service(name, tagFilter, opts)
if err != nil {
@@ -204,7 +225,22 @@ func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.Catalo
}
healthServices, _, err := p.client.Health().Service(name, tagFilter, false, opts)
return consulServices, healthServices, err
if err != nil {
return nil, nil, err
}
// Index status by service and node so it can be retrieved from a CatalogService even if the health and services
// are not in sync.
statuses := make(map[string]string)
for _, health := range healthServices {
if health.Service == nil || health.Node == nil {
continue
}
statuses[health.Node.ID+health.Service.ID] = health.Checks.AggregatedStatus()
}
return consulServices, statuses, err
}
func (p *Provider) fetchServices(ctx context.Context) ([]string, error) {

View File

@@ -2,6 +2,7 @@ package docker
import (
"context"
"errors"
"fmt"
"io"
"net"
@@ -307,7 +308,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
startStopHandle(event)
}
case err := <-errc:
if err == io.EOF {
if errors.Is(err, io.EOF) {
logger.Debug("Provider event stream closed")
}
return err

View File

@@ -307,6 +307,13 @@ func (p Provider) getIPAddress(instance ecsInstance) string {
func getPort(instance ecsInstance, serverPort string) string {
if len(serverPort) > 0 {
for _, port := range instance.machine.ports {
containerPort := strconv.FormatInt(port.containerPort, 10)
if serverPort == containerPort {
return strconv.FormatInt(port.hostPort, 10)
}
}
return serverPort
}

View File

@@ -1721,13 +1721,13 @@ func Test_buildConfiguration(t *testing.T) {
name("Test"),
labels(map[string]string{
"traefik.http.services.Service1.LoadBalancer.server.scheme": "h2c",
"traefik.http.services.Service1.LoadBalancer.server.port": "8080",
"traefik.http.services.Service1.LoadBalancer.server.port": "80",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(0, 80, "tcp"),
mPort(80, 8080, "tcp"),
),
),
),
@@ -1764,6 +1764,125 @@ func Test_buildConfiguration(t *testing.T) {
},
},
},
{
desc: "one container with label port not exposed by container",
containers: []ecsInstance{
instance(
name("Test"),
labels(map[string]string{
"traefik.http.services.Service1.LoadBalancer.server.scheme": "h2c",
"traefik.http.services.Service1.LoadBalancer.server.port": "8040",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(80, 8080, "tcp"),
),
),
),
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Service1",
Rule: "Host(`Test.traefik.wtf`)",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Service1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "h2c://127.0.0.1:8040",
},
},
PassHostHeader: Bool(true),
},
},
},
},
},
},
{
desc: "one container with label and multiple ports",
containers: []ecsInstance{
instance(
name("Test"),
labels(map[string]string{
"traefik.http.routers.Test.rule": "Host(`Test.traefik.wtf`)",
"traefik.http.routers.Test.service": "Service1",
"traefik.http.services.Service1.LoadBalancer.server.port": "4445",
"traefik.http.routers.Test2.rule": "Host(`Test.traefik.local`)",
"traefik.http.routers.Test2.service": "Service2",
"traefik.http.services.Service2.LoadBalancer.server.port": "4444",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(4444, 32123, "tcp"),
mPort(4445, 32124, "tcp"),
),
),
),
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Service1",
Rule: "Host(`Test.traefik.wtf`)",
},
"Test2": {
Service: "Service2",
Rule: "Host(`Test.traefik.local`)",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Service1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:32124",
},
},
PassHostHeader: Bool(true),
},
},
"Service2": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:32123",
},
},
PassHostHeader: Bool(true),
},
},
},
},
},
},
{
desc: "one container with label port on two services",
containers: []ecsInstance{
@@ -2274,13 +2393,13 @@ func Test_buildConfiguration(t *testing.T) {
labels(map[string]string{
"traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)",
"traefik.tcp.routers.foo.tls.options": "foo",
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
"traefik.tcp.services.foo.loadbalancer.server.port": "80",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(0, 80, "tcp"),
mPort(80, 8080, "tcp"),
),
),
),
@@ -2327,13 +2446,13 @@ func Test_buildConfiguration(t *testing.T) {
name("Test"),
labels(map[string]string{
"traefik.udp.routers.foo.entrypoints": "mydns",
"traefik.udp.services.foo.loadbalancer.server.port": "8080",
"traefik.udp.services.foo.loadbalancer.server.port": "80",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(0, 80, "udp"),
mPort(80, 8080, "udp"),
),
),
),
@@ -2506,14 +2625,14 @@ func Test_buildConfiguration(t *testing.T) {
instance(
name("Test"),
labels(map[string]string{
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
"traefik.tcp.services.foo.loadbalancer.server.port": "80",
"traefik.tcp.services.foo.loadbalancer.terminationdelay": "200",
}),
iMachine(
mState(ec2.InstanceStateNameRunning),
mPrivateIP("127.0.0.1"),
mPorts(
mPort(0, 80, "tcp"),
mPort(80, 8080, "tcp"),
),
),
),

View File

@@ -151,35 +151,25 @@ func (p Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.P
operation := func() error {
awsClient, err := p.createClient(logger)
if err != nil {
return err
return fmt.Errorf("unable to create AWS client: %w", err)
}
configuration, err := p.loadECSConfig(ctxLog, awsClient)
err = p.loadConfiguration(ctxLog, awsClient, configurationChan)
if err != nil {
return err
return fmt.Errorf("failed to get ECS configuration: %w", err)
}
configurationChan <- dynamic.Message{
ProviderName: "ecs",
Configuration: configuration,
}
reload := time.NewTicker(time.Second * time.Duration(p.RefreshSeconds))
defer reload.Stop()
ticker := time.NewTicker(time.Second * time.Duration(p.RefreshSeconds))
defer ticker.Stop()
for {
select {
case <-reload.C:
configuration, err := p.loadECSConfig(ctxLog, awsClient)
case <-ticker.C:
err = p.loadConfiguration(ctxLog, awsClient, configurationChan)
if err != nil {
logger.Errorf("Failed to load ECS configuration, error %s", err)
return err
return fmt.Errorf("failed to refresh ECS configuration: %w", err)
}
configurationChan <- dynamic.Message{
ProviderName: "ecs",
Configuration: configuration,
}
case <-routineCtx.Done():
return nil
}
@@ -198,6 +188,20 @@ func (p Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.P
return nil
}
func (p *Provider) loadConfiguration(ctx context.Context, client *awsClient, configurationChan chan<- dynamic.Message) error {
instances, err := p.listInstances(ctx, client)
if err != nil {
return err
}
configurationChan <- dynamic.Message{
ProviderName: "ecs",
Configuration: p.buildConfiguration(ctx, instances),
}
return nil
}
// Find all running Provider tasks in a cluster, also collect the task definitions (for docker labels)
// and the EC2 instance data.
func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsInstance, error) {
@@ -365,15 +369,6 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
return instances, nil
}
func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*dynamic.Configuration, error) {
instances, err := p.listInstances(ctx, client)
if err != nil {
return nil, err
}
return p.buildConfiguration(ctx, instances), nil
}
func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]*ecs.Task) (map[string]*ec2.Instance, error) {
logger := log.FromContext(ctx)
instanceIds := make(map[string]string)

View File

@@ -24,9 +24,9 @@ var _ provider.Provider = (*Provider)(nil)
// Provider is a provider.Provider implementation that queries an HTTP(s) endpoint for a configuration.
type Provider struct {
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint" export:"true"`
PollInterval ptypes.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty"`
PollTimeout ptypes.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty"`
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint"`
PollInterval ptypes.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty" export:"true"`
PollTimeout ptypes.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty" export:"true"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
httpClient *http.Client
lastConfigurationHash uint64

View File

@@ -17,7 +17,6 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
)
@@ -61,13 +60,14 @@ type Client interface {
// TODO: add tests for the clientWrapper (and its methods) itself.
type clientWrapper struct {
csCrd *versioned.Clientset
csKube *kubernetes.Clientset
csCrd versioned.Interface
csKube kubernetes.Interface
factoriesCrd map[string]externalversions.SharedInformerFactory
factoriesKube map[string]informers.SharedInformerFactory
factoriesCrd map[string]externalversions.SharedInformerFactory
factoriesKube map[string]informers.SharedInformerFactory
factoriesSecret map[string]informers.SharedInformerFactory
labelSelector labels.Selector
labelSelector string
isNamespaceAll bool
watchedNamespaces []string
@@ -87,12 +87,13 @@ func createClientFromConfig(c *rest.Config) (*clientWrapper, error) {
return newClientImpl(csKube, csCrd), nil
}
func newClientImpl(csKube *kubernetes.Clientset, csCrd *versioned.Clientset) *clientWrapper {
func newClientImpl(csKube kubernetes.Interface, csCrd versioned.Interface) *clientWrapper {
return &clientWrapper{
csCrd: csCrd,
csKube: csKube,
factoriesCrd: make(map[string]externalversions.SharedInformerFactory),
factoriesKube: make(map[string]informers.SharedInformerFactory),
csCrd: csCrd,
csKube: csKube,
factoriesCrd: make(map[string]externalversions.SharedInformerFactory),
factoriesKube: make(map[string]informers.SharedInformerFactory),
factoriesSecret: make(map[string]informers.SharedInformerFactory),
}
}
@@ -147,16 +148,25 @@ func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrappe
// WatchAll starts namespace-specific controllers for all relevant kinds.
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
eventCh := make(chan interface{}, 1)
eventHandler := c.newResourceEventHandler(eventCh)
eventHandler := &resourceEventHandler{ev: eventCh}
if len(namespaces) == 0 {
namespaces = []string{metav1.NamespaceAll}
c.isNamespaceAll = true
}
c.watchedNamespaces = namespaces
notOwnedByHelm := func(opts *metav1.ListOptions) {
opts.LabelSelector = "owner!=helm"
}
matchesLabelSelector := func(opts *metav1.ListOptions) {
opts.LabelSelector = c.labelSelector
}
for _, ns := range namespaces {
factoryCrd := externalversions.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, externalversions.WithNamespace(ns))
factoryCrd := externalversions.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, externalversions.WithNamespace(ns), externalversions.WithTweakListOptions(matchesLabelSelector))
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
@@ -166,18 +176,21 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factoryCrd.Traefik().V1alpha1().TraefikServices().Informer().AddEventHandler(eventHandler)
factoryKube := informers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, informers.WithNamespace(ns))
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
factorySecret := informers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(notOwnedByHelm))
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factoriesCrd[ns] = factoryCrd
c.factoriesKube[ns] = factoryKube
c.factoriesSecret[ns] = factorySecret
}
for _, ns := range namespaces {
c.factoriesCrd[ns].Start(stopCh)
c.factoriesKube[ns].Start(stopCh)
c.factoriesSecret[ns].Start(stopCh)
}
for _, ns := range namespaces {
@@ -192,6 +205,12 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
}
}
for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
if !ok {
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
}
}
}
return eventCh, nil
@@ -201,7 +220,7 @@ func (c *clientWrapper) GetIngressRoutes() []*v1alpha1.IngressRoute {
var result []*v1alpha1.IngressRoute
for ns, factory := range c.factoriesCrd {
ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(c.labelSelector)
ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list ingress routes in namespace %s: %v", ns, err)
}
@@ -215,7 +234,7 @@ func (c *clientWrapper) GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP {
var result []*v1alpha1.IngressRouteTCP
for ns, factory := range c.factoriesCrd {
ings, err := factory.Traefik().V1alpha1().IngressRouteTCPs().Lister().List(c.labelSelector)
ings, err := factory.Traefik().V1alpha1().IngressRouteTCPs().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list tcp ingress routes in namespace %s: %v", ns, err)
}
@@ -229,7 +248,7 @@ func (c *clientWrapper) GetIngressRouteUDPs() []*v1alpha1.IngressRouteUDP {
var result []*v1alpha1.IngressRouteUDP
for ns, factory := range c.factoriesCrd {
ings, err := factory.Traefik().V1alpha1().IngressRouteUDPs().Lister().List(c.labelSelector)
ings, err := factory.Traefik().V1alpha1().IngressRouteUDPs().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list udp ingress routes in namespace %s: %v", ns, err)
}
@@ -243,7 +262,7 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
var result []*v1alpha1.Middleware
for ns, factory := range c.factoriesCrd {
middlewares, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(c.labelSelector)
middlewares, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list middlewares in namespace %s: %v", ns, err)
}
@@ -269,7 +288,7 @@ func (c *clientWrapper) GetTraefikServices() []*v1alpha1.TraefikService {
var result []*v1alpha1.TraefikService
for ns, factory := range c.factoriesCrd {
ings, err := factory.Traefik().V1alpha1().TraefikServices().Lister().List(c.labelSelector)
ings, err := factory.Traefik().V1alpha1().TraefikServices().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list Traefik services in namespace %s: %v", ns, err)
}
@@ -284,7 +303,7 @@ func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
var result []*v1alpha1.TLSOption
for ns, factory := range c.factoriesCrd {
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(c.labelSelector)
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list tls options in namespace %s: %v", ns, err)
}
@@ -299,7 +318,7 @@ func (c *clientWrapper) GetTLSStores() []*v1alpha1.TLSStore {
var result []*v1alpha1.TLSStore
for ns, factory := range c.factoriesCrd {
stores, err := factory.Traefik().V1alpha1().TLSStores().Lister().List(c.labelSelector)
stores, err := factory.Traefik().V1alpha1().TLSStores().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list tls stores in namespace %s: %v", ns, err)
}
@@ -337,7 +356,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
}
secret, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
secret, err := c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
exist, err := translateNotFoundError(err)
return secret, exist, err
}
@@ -355,31 +374,6 @@ func (c *clientWrapper) lookupNamespace(ns string) string {
return ns
}
func (c *clientWrapper) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
return &cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
// Ignore Ingresses that do not match our custom label selector.
switch v := obj.(type) {
case *v1alpha1.IngressRoute:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
case *v1alpha1.IngressRouteTCP:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
case *v1alpha1.TraefikService:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
case *v1alpha1.TLSOption:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
case *v1alpha1.TLSStore:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
case *v1alpha1.Middleware:
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
default:
return true
}
},
Handler: &resourceEventHandler{ev: events},
}
}
// eventHandlerFunc will pass the obj on to the events channel or drop it.
// This is so passing the events along won't block in the case of high volume.
// The events are only used for signaling anyway so dropping a few is ok.

View File

@@ -0,0 +1,65 @@
package crd
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
crdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubefake "k8s.io/client-go/kubernetes/fake"
)
func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "secret",
},
}
helmSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "helm-secret",
Labels: map[string]string{
"owner": "helm",
},
},
}
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
crdClient := crdfake.NewSimpleClientset()
client := newClientImpl(kubeClient, crdClient)
stopCh := make(chan struct{})
eventCh, err := client.WatchAll(nil, stopCh)
require.NoError(t, err)
select {
case event := <-eventCh:
secret, ok := event.(*corev1.Secret)
require.True(t, ok)
assert.NotEqual(t, "helm-secret", secret.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for secret")
}
select {
case <-eventCh:
assert.Fail(t, "received more than one event")
case <-time.After(50 * time.Millisecond):
}
_, found, err := client.GetSecret("default", "secret")
require.NoError(t, err)
assert.True(t, found)
_, found, err = client.GetSecret("default", "helm-secret")
require.NoError(t, err)
assert.False(t, found)
}

View File

@@ -45,16 +45,16 @@ type Provider struct {
Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
lastConfiguration safe.Safe
}
func (p *Provider) newK8sClient(ctx context.Context, labelSelector string) (*clientWrapper, error) {
labelSel, err := labels.Parse(labelSelector)
func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
_, err := labels.Parse(p.LabelSelector)
if err != nil {
return nil, fmt.Errorf("invalid label selector: %q", labelSelector)
return nil, fmt.Errorf("invalid label selector: %q", p.LabelSelector)
}
log.FromContext(ctx).Infof("label selector is: %q", labelSel)
log.FromContext(ctx).Infof("label selector is: %q", p.LabelSelector)
withEndpoint := ""
if p.Endpoint != "" {
@@ -74,11 +74,12 @@ func (p *Provider) newK8sClient(ctx context.Context, labelSelector string) (*cli
client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
}
if err == nil {
client.labelSelector = labelSel
if err != nil {
return nil, err
}
return client, err
client.labelSelector = p.LabelSelector
return client, nil
}
// Init the provider.
@@ -92,8 +93,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
ctxLog := log.With(context.Background(), log.Str(log.ProviderName, providerName))
logger := log.FromContext(ctxLog)
logger.Debugf("Using label selector: %q", p.LabelSelector)
k8sClient, err := p.newK8sClient(ctxLog, p.LabelSelector)
k8sClient, err := p.newK8sClient(ctxLog)
if err != nil {
return err
}

View File

@@ -19,7 +19,6 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
)
@@ -59,10 +58,12 @@ type Client interface {
}
type clientWrapper struct {
clientset *kubernetes.Clientset
factories map[string]informers.SharedInformerFactory
clientset kubernetes.Interface
factoriesKube map[string]informers.SharedInformerFactory
factoriesSecret map[string]informers.SharedInformerFactory
factoriesIngress map[string]informers.SharedInformerFactory
clusterFactory informers.SharedInformerFactory
ingressLabelSelector labels.Selector
ingressLabelSelector string
isNamespaceAll bool
watchedNamespaces []string
}
@@ -123,17 +124,19 @@ func createClientFromConfig(c *rest.Config) (*clientWrapper, error) {
return newClientImpl(clientset), nil
}
func newClientImpl(clientset *kubernetes.Clientset) *clientWrapper {
func newClientImpl(clientset kubernetes.Interface) *clientWrapper {
return &clientWrapper{
clientset: clientset,
factories: make(map[string]informers.SharedInformerFactory),
clientset: clientset,
factoriesSecret: make(map[string]informers.SharedInformerFactory),
factoriesIngress: make(map[string]informers.SharedInformerFactory),
factoriesKube: make(map[string]informers.SharedInformerFactory),
}
}
// WatchAll starts namespace-specific controllers for all relevant kinds.
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
eventCh := make(chan interface{}, 1)
eventHandler := c.newResourceEventHandler(eventCh)
eventHandler := &resourceEventHandler{eventCh}
if len(namespaces) == 0 {
namespaces = []string{metav1.NamespaceAll}
@@ -142,21 +145,49 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
c.watchedNamespaces = namespaces
for _, ns := range namespaces {
factory := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns))
factory.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factories[ns] = factory
notOwnedByHelm := func(opts *metav1.ListOptions) {
opts.LabelSelector = "owner!=helm"
}
matchesLabelSelector := func(opts *metav1.ListOptions) {
opts.LabelSelector = c.ingressLabelSelector
}
for _, ns := range namespaces {
c.factories[ns].Start(stopCh)
factoryIngress := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(matchesLabelSelector))
factoryIngress.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
c.factoriesIngress[ns] = factoryIngress
factoryKube := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns))
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
c.factoriesKube[ns] = factoryKube
factorySecret := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(notOwnedByHelm))
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factoriesSecret[ns] = factorySecret
}
for _, ns := range namespaces {
for typ, ok := range c.factories[ns].WaitForCacheSync(stopCh) {
c.factoriesIngress[ns].Start(stopCh)
c.factoriesKube[ns].Start(stopCh)
c.factoriesSecret[ns].Start(stopCh)
}
for _, ns := range namespaces {
for typ, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) {
if !ok {
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
}
}
for typ, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
if !ok {
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
}
}
for typ, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
if !ok {
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
}
@@ -188,9 +219,9 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
func (c *clientWrapper) GetIngresses() []*networkingv1beta1.Ingress {
var results []*networkingv1beta1.Ingress
for ns, factory := range c.factories {
for ns, factory := range c.factoriesIngress {
// extensions
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
}
@@ -205,7 +236,7 @@ func (c *clientWrapper) GetIngresses() []*networkingv1beta1.Ingress {
}
// networking
list, err := factory.Networking().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
list, err := factory.Networking().V1beta1().Ingresses().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
}
@@ -239,7 +270,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1beta1.Ingress, ip,
return c.updateIngressStatusOld(src, ip, hostname)
}
ing, err := c.factories[c.lookupNamespace(src.Namespace)].Networking().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Networking().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
if err != nil {
return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
}
@@ -268,7 +299,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1beta1.Ingress, ip,
}
func (c *clientWrapper) updateIngressStatusOld(src *networkingv1beta1.Ingress, ip, hostname string) error {
ing, err := c.factories[c.lookupNamespace(src.Namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
if err != nil {
return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
}
@@ -302,7 +333,7 @@ func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, boo
return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name)
}
service, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name)
service, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name)
exist, err := translateNotFoundError(err)
return service, exist, err
}
@@ -313,7 +344,7 @@ func (c *clientWrapper) GetEndpoints(namespace, name string) (*corev1.Endpoints,
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
}
endpoint, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name)
endpoint, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name)
exist, err := translateNotFoundError(err)
return endpoint, exist, err
}
@@ -324,7 +355,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
}
secret, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
secret, err := c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
exist, err := translateNotFoundError(err)
return secret, exist, err
}
@@ -361,25 +392,6 @@ func (c *clientWrapper) lookupNamespace(ns string) string {
return ns
}
func (c *clientWrapper) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
return &cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
// Ignore Ingresses that do not match our custom label selector.
switch v := obj.(type) {
case *extensionsv1beta1.Ingress:
lbls := labels.Set(v.GetLabels())
return c.ingressLabelSelector.Matches(lbls)
case *networkingv1beta1.Ingress:
lbls := labels.Set(v.GetLabels())
return c.ingressLabelSelector.Matches(lbls)
default:
return true
}
},
Handler: &resourceEventHandler{ev: events},
}
}
// GetServerVersion returns the cluster server version, or an error.
func (c *clientWrapper) GetServerVersion() (*version.Version, error) {
serverVersion, err := c.clientset.Discovery().ServerVersion()

View File

@@ -3,10 +3,15 @@ package ingress
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
kubeerror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
kubefake "k8s.io/client-go/kubernetes/fake"
)
func TestTranslateNotFoundError(t *testing.T) {
@@ -47,3 +52,54 @@ func TestTranslateNotFoundError(t *testing.T) {
})
}
}
func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "secret",
},
}
helmSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "helm-secret",
Labels: map[string]string{
"owner": "helm",
},
},
}
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
client := newClientImpl(kubeClient)
stopCh := make(chan struct{})
eventCh, err := client.WatchAll(nil, stopCh)
require.NoError(t, err)
select {
case event := <-eventCh:
secret, ok := event.(*corev1.Secret)
require.True(t, ok)
assert.NotEqual(t, "helm-secret", secret.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for secret")
}
select {
case <-eventCh:
assert.Fail(t, "received more than one event")
case <-time.After(50 * time.Millisecond):
}
_, found, err := client.GetSecret("default", "secret")
require.NoError(t, err)
assert.True(t, found)
_, found, err = client.GetSecret("default", "helm-secret")
require.NoError(t, err)
assert.False(t, found)
}

View File

@@ -41,8 +41,8 @@ type Provider struct {
Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty"`
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty" export:"true"`
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
lastConfiguration safe.Safe
}
@@ -53,15 +53,15 @@ type EndpointIngress struct {
PublishedService string `description:"Published Kubernetes Service to copy status from." json:"publishedService,omitempty" toml:"publishedService,omitempty" yaml:"publishedService,omitempty"`
}
func (p *Provider) newK8sClient(ctx context.Context, ingressLabelSelector string) (*clientWrapper, error) {
ingLabelSel, err := labels.Parse(ingressLabelSelector)
func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
_, err := labels.Parse(p.LabelSelector)
if err != nil {
return nil, fmt.Errorf("invalid ingress label selector: %q", ingressLabelSelector)
return nil, fmt.Errorf("invalid ingress label selector: %q", p.LabelSelector)
}
logger := log.FromContext(ctx)
logger.Infof("ingress label selector is: %q", ingLabelSel)
logger.Infof("ingress label selector is: %q", p.LabelSelector)
withEndpoint := ""
if p.Endpoint != "" {
@@ -81,11 +81,12 @@ func (p *Provider) newK8sClient(ctx context.Context, ingressLabelSelector string
cl, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
}
if err == nil {
cl.ingressLabelSelector = ingLabelSel
if err != nil {
return nil, err
}
return cl, err
cl.ingressLabelSelector = p.LabelSelector
return cl, nil
}
// Init the provider.
@@ -99,8 +100,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
ctxLog := log.With(context.Background(), log.Str(log.ProviderName, "kubernetes"))
logger := log.FromContext(ctxLog)
logger.Debugf("Using Ingress label selector: %q", p.LabelSelector)
k8sClient, err := p.newK8sClient(ctxLog, p.LabelSelector)
k8sClient, err := p.newK8sClient(ctxLog)
if err != nil {
return err
}

View File

@@ -10,7 +10,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
kv.Provider
kv.Provider `export:"true"`
}
// SetDefaults sets the default values.

View File

@@ -10,7 +10,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
kv.Provider
kv.Provider `export:"true"`
}
// SetDefaults sets the default values.

View File

@@ -10,7 +10,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
kv.Provider
kv.Provider `export:"true"`
}
// SetDefaults sets the default values.

View File

@@ -10,7 +10,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
kv.Provider
kv.Provider `export:"true"`
}
// SetDefaults sets the default values.

View File

@@ -49,10 +49,10 @@ type Provider struct {
Constraints string `description:"Constraints is an expression that Traefik matches against the application's labels to determine whether to create any route for that application." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
Trace bool `description:"Display additional provider logs." json:"trace,omitempty" toml:"trace,omitempty" yaml:"trace,omitempty" export:"true"`
Watch bool `description:"Watch provider." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"`
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
ExposedByDefault bool `description:"Expose Marathon apps by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." json:"dcosToken,omitempty" toml:"dcosToken,omitempty" yaml:"dcosToken,omitempty" export:"true"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." json:"dcosToken,omitempty" toml:"dcosToken,omitempty" yaml:"dcosToken,omitempty"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
DialerTimeout ptypes.Duration `description:"Set a dialer timeout for Marathon." json:"dialerTimeout,omitempty" toml:"dialerTimeout,omitempty" yaml:"dialerTimeout,omitempty" export:"true"`
ResponseHeaderTimeout ptypes.Duration `description:"Set a response header timeout for Marathon." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`

View File

@@ -46,7 +46,7 @@ type Provider struct {
ExposedByDefault bool `description:"Expose containers by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"`
EnableServiceHealthFilter bool `description:"Filter services with unhealthy states and inactive states." json:"enableServiceHealthFilter,omitempty" toml:"enableServiceHealthFilter,omitempty" yaml:"enableServiceHealthFilter,omitempty" export:"true"`
RefreshSeconds int `description:"Defines the polling interval in seconds." json:"refreshSeconds,omitempty" toml:"refreshSeconds,omitempty" yaml:"refreshSeconds,omitempty" export:"true"`
IntervalPoll bool `description:"Poll the Rancher metadata service every 'rancher.refreshseconds' (less accurate)." json:"intervalPoll,omitempty" toml:"intervalPoll,omitempty" yaml:"intervalPoll,omitempty"`
IntervalPoll bool `description:"Poll the Rancher metadata service every 'rancher.refreshseconds' (less accurate)." json:"intervalPoll,omitempty" toml:"intervalPoll,omitempty" yaml:"intervalPoll,omitempty" export:"true"`
Prefix string `description:"Prefix used for accessing the Rancher metadata service." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty"`
defaultRuleTpl *template.Template
}

View File

@@ -2,6 +2,7 @@ package server
import (
"context"
"errors"
"os"
"os/signal"
"time"
@@ -85,9 +86,9 @@ func (s *Server) Close() {
go func(ctx context.Context) {
<-ctx.Done()
if ctx.Err() == context.Canceled {
if errors.Is(ctx.Err(), context.Canceled) {
return
} else if ctx.Err() == context.DeadlineExceeded {
} else if errors.Is(ctx.Err(), context.DeadlineExceeded) {
panic("Timeout while stopping traefik, killing instance ✝")
}
}(ctx)

View File

@@ -2,11 +2,13 @@ package server
import (
"context"
"errors"
"fmt"
stdlog "log"
"net"
"net/http"
"sync"
"syscall"
"time"
proxyprotocol "github.com/c0va23/go-proxyprotocol"
@@ -172,11 +174,15 @@ func (e *TCPEntryPoint) Start(ctx context.Context) {
conn, err := e.listener.Accept()
if err != nil {
logger.Error(err)
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Temporary() {
continue
}
e.httpServer.Forwarder.errChan <- err
e.httpsServer.Forwarder.errChan <- err
return
}
@@ -230,7 +236,7 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) {
if err == nil {
return
}
if ctx.Err() == context.DeadlineExceeded {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
logger.Debugf("Server failed to shutdown within deadline because: %s", err)
if err = server.Close(); err != nil {
logger.Error(err)
@@ -261,7 +267,7 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) {
if err == nil {
return
}
if ctx.Err() == context.DeadlineExceeded {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
logger.Debugf("Server failed to shutdown before deadline because: %s", err)
}
e.tracker.Close()
@@ -340,7 +346,11 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
}
if err = tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
return nil, err
// Some systems, such as OpenBSD, have no user-settable per-socket TCP
// keepalive options.
if !errors.Is(err, syscall.ENOPROTOOPT) {
return nil, err
}
}
return tc, nil

View File

@@ -46,7 +46,7 @@ func TestShutdownTCP(t *testing.T) {
for {
_, err := http.ReadRequest(bufio.NewReader(conn))
if err == io.EOF || (err != nil && strings.HasSuffix(err.Error(), "use of closed network connection")) {
if errors.Is(err, io.EOF) || (err != nil && strings.HasSuffix(err.Error(), "use of closed network connection")) {
return
}
require.NoError(t, err)

View File

@@ -81,13 +81,13 @@ func (m *Mirroring) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
logger := log.FromContext(req.Context())
rr, bytesRead, err := newReusableRequest(req, m.maxBodySize)
if err != nil && err != errBodyTooLarge {
if err != nil && !errors.Is(err, errBodyTooLarge) {
http.Error(rw, http.StatusText(http.StatusInternalServerError)+
fmt.Sprintf("error creating reusable request: %v", err), http.StatusInternalServerError)
return
}
if err == errBodyTooLarge {
if errors.Is(err, errBodyTooLarge) {
req.Body = ioutil.NopCloser(io.MultiReader(bytes.NewReader(bytesRead), req.Body))
m.handler.ServeHTTP(rw, req)
logger.Debugf("no mirroring, request body larger than allowed size")
@@ -196,13 +196,13 @@ func newReusableRequest(req *http.Request, maxBodySize int64) (*reusableRequest,
// the request body is larger than what we allow for the mirrors.
body := make([]byte, maxBodySize+1)
n, err := io.ReadFull(req.Body, body)
if err != nil && err != io.ErrUnexpectedEOF {
if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
return nil, nil, err
}
// we got an ErrUnexpectedEOF, which means there was less than maxBodySize data to read,
// which permits us sending also to all the mirrors later.
if err == io.ErrUnexpectedEOF {
if errors.Is(err, io.ErrUnexpectedEOF) {
return &reusableRequest{
req: req,
body: body[:n],

View File

@@ -2,6 +2,7 @@ package wrr
import (
"container/heap"
"errors"
"fmt"
"net/http"
"sync"
@@ -105,7 +106,7 @@ func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if b.stickyCookie != nil {
cookie, err := req.Cookie(b.stickyCookie.name)
if err != nil && err != http.ErrNoCookie {
if err != nil && !errors.Is(err, http.ErrNoCookie) {
log.WithoutContext().Warnf("Error while reading cookie: %v", err)
}

View File

@@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"fmt"
"io"
"net"
@@ -83,13 +84,14 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
statusCode := http.StatusInternalServerError
switch {
case err == io.EOF:
case errors.Is(err, io.EOF):
statusCode = http.StatusBadGateway
case err == context.Canceled:
case errors.Is(err, context.Canceled):
statusCode = StatusClientClosedRequest
default:
if e, ok := err.(net.Error); ok {
if e.Timeout() {
var netErr net.Error
if errors.As(err, &netErr) {
if netErr.Timeout() {
statusCode = http.StatusGatewayTimeout
} else {
statusCode = http.StatusBadGateway

View File

@@ -3,6 +3,7 @@ package service
import (
"bufio"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
@@ -49,12 +50,13 @@ func TestWebSocketTCPClose(t *testing.T) {
withPath("/ws"),
).open()
require.NoError(t, err)
conn.Close()
serverErr := <-errChan
wsErr, ok := serverErr.(*gorillawebsocket.CloseError)
assert.Equal(t, true, ok)
var wsErr *gorillawebsocket.CloseError
require.True(t, errors.As(serverErr, &wsErr))
assert.Equal(t, 1006, wsErr.Code)
}
@@ -119,7 +121,7 @@ func TestWebSocketPingPong(t *testing.T) {
_, _, err = conn.ReadMessage()
if err != goodErr {
if !errors.Is(err, goodErr) {
require.NoError(t, err)
}
}

View File

@@ -10,8 +10,10 @@ import (
// Proxy forwards a TCP request to a TCP service.
type Proxy struct {
address string
target *net.TCPAddr
terminationDelay time.Duration
refreshTarget bool
}
// NewProxy creates a new Proxy.
@@ -21,7 +23,18 @@ func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
return nil, err
}
return &Proxy{target: tcpAddr, terminationDelay: terminationDelay}, nil
// enable the refresh of the target only if the address in an IP
refreshTarget := false
if host, _, err := net.SplitHostPort(address); err == nil && net.ParseIP(host) == nil {
refreshTarget = true
}
return &Proxy{
address: address,
target: tcpAddr,
refreshTarget: refreshTarget,
terminationDelay: terminationDelay,
}, nil
}
// ServeTCP forwards the connection to a service.
@@ -31,6 +44,15 @@ func (p *Proxy) ServeTCP(conn WriteCloser) {
// needed because of e.g. server.trackedConnection
defer conn.Close()
if p.refreshTarget {
tcpAddr, err := net.ResolveTCPAddr("tcp", p.address)
if err != nil {
log.Errorf("Error resolving tcp address: %v", err)
return
}
p.target = tcpAddr
}
connBackend, err := net.DialTCP("tcp", nil, p.target)
if err != nil {
log.Errorf("Error while connection to backend: %v", err)

View File

@@ -5,9 +5,11 @@ import (
"fmt"
"io"
"net"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -79,3 +81,74 @@ func TestCloseWrite(t *testing.T) {
require.Equal(t, int64(4), n)
require.Equal(t, "PONG", buffer.String())
}
func TestLookupAddress(t *testing.T) {
testCases := []struct {
desc string
address string
expectSame assert.ComparisonAssertionFunc
}{
{
desc: "IP doesn't need refresh",
address: "8.8.4.4:53",
expectSame: assert.Same,
},
{
desc: "Hostname needs refresh",
address: "dns.google:53",
expectSame: assert.NotSame,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
proxy, err := NewProxy(test.address, 10*time.Millisecond)
require.NoError(t, err)
require.NotNil(t, proxy.target)
proxyListener, err := net.Listen("tcp", ":0")
require.NoError(t, err)
var wg sync.WaitGroup
go func(wg *sync.WaitGroup) {
for {
conn, err := proxyListener.Accept()
require.NoError(t, err)
proxy.ServeTCP(conn.(*net.TCPConn))
wg.Done()
}
}(&wg)
var lastTarget *net.TCPAddr
for i := 0; i < 3; i++ {
wg.Add(1)
conn, err := net.Dial("tcp", proxyListener.Addr().String())
require.NoError(t, err)
_, err = conn.Write([]byte("ping\n"))
require.NoError(t, err)
err = conn.Close()
require.NoError(t, err)
wg.Wait()
assert.NotNil(t, proxy.target)
if lastTarget != nil {
test.expectSame(t, lastTarget, proxy.target)
}
lastTarget = proxy.target
}
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
"errors"
"io"
"net"
"net/http"
@@ -197,10 +198,11 @@ func (c *Conn) Read(p []byte) (n int, err error) {
func clientHelloServerName(br *bufio.Reader) (string, bool, string, error) {
hdr, err := br.Peek(1)
if err != nil {
opErr, ok := err.(*net.OpError)
if err != io.EOF && (!ok || !opErr.Timeout()) {
var opErr *net.OpError
if !errors.Is(err, io.EOF) && (!errors.As(err, &opErr) || opErr.Timeout()) {
log.WithoutContext().Debugf("Error while Peeking first byte: %s", err)
}
return "", false, "", err
}

View File

@@ -18,7 +18,7 @@ type Config struct {
LocalAgentHostPort string `description:"Set datadog-agent's host:port that the reporter will used." json:"localAgentHostPort,omitempty" toml:"localAgentHostPort,omitempty" yaml:"localAgentHostPort,omitempty"`
GlobalTag string `description:"Key:Value tag to be set on all the spans." json:"globalTag,omitempty" toml:"globalTag,omitempty" yaml:"globalTag,omitempty" export:"true"`
Debug bool `description:"Enable Datadog debug." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"`
PrioritySampling bool `description:"Enable priority sampling. When using distributed tracing, this option must be enabled in order to get all the parts of a distributed trace sampled." json:"prioritySampling,omitempty" toml:"prioritySampling,omitempty" yaml:"prioritySampling,omitempty"`
PrioritySampling bool `description:"Enable priority sampling. When using distributed tracing, this option must be enabled in order to get all the parts of a distributed trace sampled." json:"prioritySampling,omitempty" toml:"prioritySampling,omitempty" yaml:"prioritySampling,omitempty" export:"true"`
TraceIDHeaderName string `description:"Specifies the header name that will be used to store the trace ID." json:"traceIDHeaderName,omitempty" toml:"traceIDHeaderName,omitempty" yaml:"traceIDHeaderName,omitempty" export:"true"`
ParentIDHeaderName string `description:"Specifies the header name that will be used to store the parent ID." json:"parentIDHeaderName,omitempty" toml:"parentIDHeaderName,omitempty" yaml:"parentIDHeaderName,omitempty" export:"true"`
SamplingPriorityHeaderName string `description:"Specifies the header name that will be used to store the sampling priority." json:"samplingPriorityHeaderName,omitempty" toml:"samplingPriorityHeaderName,omitempty" yaml:"samplingPriorityHeaderName,omitempty" export:"true"`

View File

@@ -26,7 +26,7 @@ func init() {
type Config struct {
ServerURL string `description:"Set the URL of the Elastic APM server." json:"serverURL,omitempty" toml:"serverURL,omitempty" yaml:"serverURL,omitempty"`
SecretToken string `description:"Set the token used to connect to Elastic APM Server." json:"secretToken,omitempty" toml:"secretToken,omitempty" yaml:"secretToken,omitempty"`
ServiceEnvironment string `description:"Set the name of the environment Traefik is deployed in, e.g. 'production' or 'staging'." json:"serviceEnvironment,omitempty" toml:"serviceEnvironment,omitempty" yaml:"serviceEnvironment,omitempty"`
ServiceEnvironment string `description:"Set the name of the environment Traefik is deployed in, e.g. 'production' or 'staging'." json:"serviceEnvironment,omitempty" toml:"serviceEnvironment,omitempty" yaml:"serviceEnvironment,omitempty" export:"true"`
}
// Setup sets up the tracer.

View File

@@ -42,7 +42,7 @@ func (c *Config) SetDefaults() {
// Collector provides configuration settings for jaeger collector.
type Collector struct {
Endpoint string `description:"Instructs reporter to send spans to jaeger-collector at this URL." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"`
Endpoint string `description:"Instructs reporter to send spans to jaeger-collector at this URL." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
User string `description:"User for basic http authentication when sending spans to jaeger-collector." json:"user,omitempty" toml:"user,omitempty" yaml:"user,omitempty"`
Password string `description:"Password for basic http authentication when sending spans to jaeger-collector." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"`
}

View File

@@ -23,7 +23,7 @@ const (
type TraefikLog struct {
Level string `description:"Log level set to traefik logs." json:"level,omitempty" toml:"level,omitempty" yaml:"level,omitempty" export:"true"`
FilePath string `description:"Traefik log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
Format string `description:"Traefik log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty"`
Format string `description:"Traefik log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@@ -34,7 +34,7 @@ func (l *TraefikLog) SetDefaults() {
// AccessLog holds the configuration settings for the access logger (middlewares/accesslog).
type AccessLog struct {
FilePath string `description:"Access log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty" export:"true"`
FilePath string `description:"Access log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
Format string `description:"Access log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
Filters *AccessLogFilters `description:"Access log filters, used to keep only specific access logs." json:"filters,omitempty" toml:"filters,omitempty" yaml:"filters,omitempty" export:"true"`
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`

View File

@@ -8,10 +8,10 @@ import (
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
type Metrics struct {
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." json:"influxDB,omitempty" toml:"influxDB,omitempty" yaml:"influxDB,omitempty" label:"allowEmpty" file:"allowEmpty"`
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." json:"influxDB,omitempty" toml:"influxDB,omitempty" yaml:"influxDB,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// Prometheus can contain specific configuration used by the Prometheus Metrics exporter.
@@ -20,7 +20,7 @@ type Prometheus struct {
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"`
ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty"`
ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@@ -72,8 +72,8 @@ type InfluxDB struct {
PushInterval types.Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"`
RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"`
Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"`
Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"`
Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"`
Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"`
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
}

View File

@@ -15,10 +15,10 @@ import (
// CA, Cert and Key can be either path or file contents.
type ClientTLS struct {
CA string `description:"TLS CA" json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"`
CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty"`
CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"`
Cert string `description:"TLS cert" json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"`
Key string `description:"TLS key" json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"`
InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
}
// CreateTLSConfig creates a TLS config from ClientTLS structures.

View File

@@ -1,6 +1,7 @@
package udp
import (
"errors"
"io"
"net"
"testing"
@@ -24,7 +25,7 @@ func TestConsecutiveWrites(t *testing.T) {
go func() {
for {
conn, err := ln.Accept()
if err == errClosedListener {
if errors.Is(err, errClosedListener) {
return
}
require.NoError(t, err)
@@ -86,7 +87,7 @@ func TestListenNotBlocking(t *testing.T) {
go func() {
for {
conn, err := ln.Accept()
if err == errClosedListener {
if errors.Is(err, errClosedListener) {
return
}
require.NoError(t, err)
@@ -183,7 +184,7 @@ func testTimeout(t *testing.T, withRead bool) {
go func() {
for {
conn, err := ln.Accept()
if err == errClosedListener {
if errors.Is(err, errClosedListener) {
return
}
require.NoError(t, err)

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e
if ! test -e autogen/genstatic/gen.go; then

View File

@@ -4,11 +4,11 @@ RepositoryName = "traefik"
OutputType = "file"
FileName = "traefik_changelog.md"
# example new bugfix v2.3.2
# example new bugfix v2.3.4
CurrentRef = "v2.3"
PreviousRef = "v2.3.1"
PreviousRef = "v2.3.3"
BaseBranch = "v2.3"
FutureCurrentRefName = "v2.3.2"
FutureCurrentRefName = "v2.3.4"
ThresholdPreviousRef = 10
ThresholdCurrentRef = 10

View File

@@ -1,4 +1,5 @@
#!/bin/bash -e
#!/usr/bin/env bash
set -e
HACK_DIR="$( cd "$( dirname "${0}" )" && pwd -P)"; export HACK_DIR
REPO_ROOT=${HACK_DIR}/..