mirror of
https://github.com/containous/traefik.git
synced 2025-09-28 09:44:21 +03:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
76e35a09b7 | ||
|
d2c1d39d42 | ||
|
e9cccf6504 | ||
|
1c505903ff | ||
|
53ed8e04ae | ||
|
be0845af02 | ||
|
f83a57b3da | ||
|
08264749f0 | ||
|
a75819cae3 | ||
|
9fb32a47ca | ||
|
4f43c9ebb4 | ||
|
9177982334 | ||
|
52eeff9f9f | ||
|
0fcccd35ff | ||
|
598dcf6b62 | ||
|
459200dd01 | ||
|
af22cabc6f | ||
|
920e82f11a | ||
|
9bdf9e1e02 | ||
|
3a45f05e36 | ||
|
8e3e387be7 | ||
|
267d0b7b5a | ||
|
f5b290b093 | ||
|
4ea1c98ac9 | ||
|
db007efe00 | ||
|
a0c02f62a3 | ||
|
ff7b814edc | ||
|
ea459e9af0 | ||
|
2dd5a53db2 | ||
|
fc97ea7ee0 | ||
|
582d2540af | ||
|
6ad79dcd45 | ||
|
721896ba70 | ||
|
228270414c | ||
|
2683df7b5b |
@@ -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]
|
||||
|
44
CHANGELOG.md
44
CHANGELOG.md
@@ -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)
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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 it’s 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
|
||||
|
||||
|
@@ -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 you’re 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).
|
||||
|
@@ -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) |
|
||||
|
@@ -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:
|
||||
|
||||
|
@@ -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`
|
||||
|
@@ -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
|
||||
|
@@ -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 [...]
|
||||
|
@@ -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"]
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -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_
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
```
|
||||
|
@@ -228,6 +228,7 @@ http:
|
||||
to-whoami-tcp:
|
||||
service: whoami-tcp
|
||||
rule: HostSNI(`whoami-tcp.example.com`)
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
whoami-tcp:
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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'
|
||||
|
@@ -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
|
||||
|
||||
|
2
docs/theme/partials/company-header.html
vendored
2
docs/theme/partials/company-header.html
vendored
@@ -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
6
go.mod
@@ -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
13
go.sum
@@ -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=
|
||||
|
@@ -3,6 +3,8 @@ kind: Ingress
|
||||
metadata:
|
||||
name: test.ingress
|
||||
namespace: default
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
rules:
|
||||
|
@@ -3,6 +3,8 @@ kind: IngressRoute
|
||||
metadata:
|
||||
name: test.route
|
||||
namespace: default
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
|
@@ -3,6 +3,8 @@ kind: TLSOption
|
||||
metadata:
|
||||
name: mytlsoption
|
||||
namespace: default
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
minVersion: VersionTLS12
|
||||
|
@@ -3,6 +3,8 @@ kind: TLSStore
|
||||
metadata:
|
||||
name: mytlsstore
|
||||
namespace: default
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
defaultCertificate:
|
||||
|
@@ -50,6 +50,8 @@ kind: IngressRoute
|
||||
metadata:
|
||||
name: api.route
|
||||
namespace: default
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
|
19
integration/fixtures/k8s_crd_label_selector.toml
Normal file
19
integration/fixtures/k8s_crd_label_selector.toml
Normal 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"
|
16
integration/fixtures/k8s_ingress_label_selector.toml
Normal file
16
integration/fixtures/k8s_ingress_label_selector.toml
Normal 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"
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -1,5 +1,5 @@
|
||||
haproxy:
|
||||
image: haproxy
|
||||
image: haproxy:2.2
|
||||
volumes:
|
||||
- ../haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
|
||||
|
||||
|
65
integration/testdata/rawdata-crd-label-selector.json
vendored
Normal file
65
integration/testdata/rawdata-crd-label-selector.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
106
integration/testdata/rawdata-ingress-label-selector.json
vendored
Normal file
106
integration/testdata/rawdata-ingress-label-selector.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
454
pkg/anonymize/testdata/anonymized-static-config.json
vendored
Normal file
454
pkg/anonymize/testdata/anonymized-static-config.json
vendored
Normal 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
82
pkg/anonymize/testdata/example.json
vendored
Normal 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
82
pkg/anonymize/testdata/expected.json
vendored
Normal 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"
|
||||
}
|
@@ -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.
|
||||
|
@@ -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"`
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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{}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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"),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
65
pkg/provider/kubernetes/crd/client_test.go
Normal file
65
pkg/provider/kubernetes/crd/client_test.go
Normal 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)
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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"`
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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],
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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"`
|
||||
|
@@ -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.
|
||||
|
@@ -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"`
|
||||
}
|
||||
|
@@ -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"`
|
||||
|
@@ -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"`
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if ! test -e autogen/genstatic/gen.go; then
|
||||
|
@@ -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
|
||||
|
@@ -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}/..
|
||||
|
Reference in New Issue
Block a user