xMerge github.com:traefik/traefik
Signed-off-by: baalajimaestro <me@baalajimaestro.me>
This commit is contained in:
commit
ab4ec9a63a
129 changed files with 5851 additions and 2916 deletions
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -2,16 +2,16 @@
|
|||
PLEASE READ THIS MESSAGE.
|
||||
|
||||
Documentation fixes or enhancements:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.9
|
||||
- for Traefik v3: use branch master
|
||||
|
||||
Bug fixes:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.9
|
||||
- for Traefik v3: use branch master
|
||||
|
||||
Enhancements:
|
||||
- for Traefik v1: we only accept bug fixes
|
||||
- for Traefik v2: use branch master
|
||||
- for Traefik v2: we only accept bug fixes
|
||||
- for Traefik v3: use branch master
|
||||
|
||||
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ blocks:
|
|||
- name: GH_VERSION
|
||||
value: 1.12.1
|
||||
- name: CODENAME
|
||||
value: "banon"
|
||||
value: "beaufort"
|
||||
- name: IN_DOCKER
|
||||
value: ""
|
||||
prologue:
|
||||
|
|
78
CHANGELOG.md
78
CHANGELOG.md
|
@ -1,3 +1,81 @@
|
|||
## [v3.0.0-beta2](https://github.com/traefik/traefik/tree/v3.0.0-beta2) (2022-12-07)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v3.0.0-beta1...v3.0.0-beta2)
|
||||
|
||||
**Enhancements:**
|
||||
- **[http3]** Moves HTTP/3 outside the experimental section ([#9570](https://github.com/traefik/traefik/pull/9570) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[logs]** Change traefik cmd error log to error level ([#9569](https://github.com/traefik/traefik/pull/9569) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[rules]** Rework Host and HostRegexp matchers ([#9559](https://github.com/traefik/traefik/pull/9559) by [tomMoulard](https://github.com/tomMoulard))
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.9 into master ([#9586](https://github.com/traefik/traefik/pull/9586) by [tomMoulard](https://github.com/tomMoulard))
|
||||
|
||||
## [v2.9.6](https://github.com/traefik/traefik/tree/v2.9.6) (2022-12-07)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.5...v2.9.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.9.1 ([#9550](https://github.com/traefik/traefik/pull/9550) by [ldez](https://github.com/ldez))
|
||||
- **[k8s/crd]** Support of allowEmptyServices in TraefikService ([#9424](https://github.com/traefik/traefik/pull/9424) by [jeromeguiard](https://github.com/jeromeguiard))
|
||||
- **[logs]** Remove logs of the request ([#9574](https://github.com/traefik/traefik/pull/9574) by [ldez](https://github.com/ldez))
|
||||
- **[plugins]** Increase the timeout on plugin download ([#9529](https://github.com/traefik/traefik/pull/9529) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Update golang.org/x/net ([#9582](https://github.com/traefik/traefik/pull/9582) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Handle broken TLS conf better ([#9572](https://github.com/traefik/traefik/pull/9572) by [mpl](https://github.com/mpl))
|
||||
- **[tracing]** Update DataDog tracing dependency to v1.43.1 ([#9526](https://github.com/traefik/traefik/pull/9526) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[webui]** Add missing serialNumber passTLSClientCert option to middleware panel ([#9539](https://github.com/traefik/traefik/pull/9539) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add networking example ([#9542](https://github.com/traefik/traefik/pull/9542) by [Janik-Haag](https://github.com/Janik-Haag))
|
||||
- **[hub]** Add information about the Hub Agent ([#9560](https://github.com/traefik/traefik/pull/9560) by [nmengin](https://github.com/nmengin))
|
||||
- **[k8s/helm]** Update Helm installation section ([#9564](https://github.com/traefik/traefik/pull/9564) by [mloiseleur](https://github.com/mloiseleur))
|
||||
- **[middleware]** Clarify PathPrefix matcher greediness ([#9519](https://github.com/traefik/traefik/pull/9519) by [mpl](https://github.com/mpl))
|
||||
|
||||
## [v3.0.0-beta1](https://github.com/traefik/traefik/tree/v3.0.0-beta1) (2022-12-05)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc1...v3.0.0-beta1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[ecs]** Add option to keep only healthy ECS tasks ([#8027](https://github.com/traefik/traefik/pull/8027) by [Michampt](https://github.com/Michampt))
|
||||
- **[healthcheck]** Support gRPC healthcheck ([#8583](https://github.com/traefik/traefik/pull/8583) by [jjacque](https://github.com/jjacque))
|
||||
- **[healthcheck]** Add a status option to the service health check ([#9463](https://github.com/traefik/traefik/pull/9463) by [guoard](https://github.com/guoard))
|
||||
- **[http]** Support custom headers when fetching configuration through HTTP ([#9421](https://github.com/traefik/traefik/pull/9421) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[logs,performance]** New logger for the Traefik logs ([#9515](https://github.com/traefik/traefik/pull/9515) by [ldez](https://github.com/ldez))
|
||||
- **[logs,plugins]** Retry on plugin API calls ([#9530](https://github.com/traefik/traefik/pull/9530) by [ldez](https://github.com/ldez))
|
||||
- **[logs,provider]** Improve provider logs ([#9562](https://github.com/traefik/traefik/pull/9562) by [ldez](https://github.com/ldez))
|
||||
- **[logs]** Improve test logger assertions ([#9533](https://github.com/traefik/traefik/pull/9533) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Support gRPC and gRPC-Web protocol in metrics ([#9483](https://github.com/traefik/traefik/pull/9483) by [longit644](https://github.com/longit644))
|
||||
- **[middleware,accesslogs]** Log TLS client subject ([#9285](https://github.com/traefik/traefik/pull/9285) by [xmessi](https://github.com/xmessi))
|
||||
- **[middleware,metrics,tracing]** Add OpenTelemetry tracing and metrics support ([#8999](https://github.com/traefik/traefik/pull/8999) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[middleware]** Disable Content-Type auto-detection by default ([#9546](https://github.com/traefik/traefik/pull/9546) by [sdelicata](https://github.com/sdelicata))
|
||||
- **[middleware]** Add gRPC-Web middleware ([#9451](https://github.com/traefik/traefik/pull/9451) by [juliens](https://github.com/juliens))
|
||||
- **[middleware]** Add support for Brotli ([#9387](https://github.com/traefik/traefik/pull/9387) by [glinton](https://github.com/glinton))
|
||||
- **[middleware]** Renaming IPWhiteList to IPAllowList ([#9457](https://github.com/traefik/traefik/pull/9457) by [wxmbugu](https://github.com/wxmbugu))
|
||||
- **[nomad]** Support multiple namespaces in the Nomad Provider ([#9332](https://github.com/traefik/traefik/pull/9332) by [0teh](https://github.com/0teh))
|
||||
- **[rules]** Update routing syntax ([#9531](https://github.com/traefik/traefik/pull/9531) by [skwair](https://github.com/skwair))
|
||||
- **[server]** Rework servers load-balancer to use the WRR ([#9431](https://github.com/traefik/traefik/pull/9431) by [juliens](https://github.com/juliens))
|
||||
- **[server]** Allow default entrypoints definition ([#9100](https://github.com/traefik/traefik/pull/9100) by [jilleJr](https://github.com/jilleJr))
|
||||
- **[tls,service]** Support SPIFFE mTLS between Traefik and Backend servers ([#9394](https://github.com/traefik/traefik/pull/9394) by [jlevesy](https://github.com/jlevesy))
|
||||
- **[tls]** Add Tailscale certificate resolver ([#9237](https://github.com/traefik/traefik/pull/9237) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[tls]** Support SNI routing with Postgres STARTTLS connections ([#9377](https://github.com/traefik/traefik/pull/9377) by [rtribotte](https://github.com/rtribotte))
|
||||
- Remove deprecated options ([#9527](https://github.com/traefik/traefik/pull/9527) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[logs]** Fix log level ([#9545](https://github.com/traefik/traefik/pull/9545) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Fix ServerUp metric ([#9534](https://github.com/traefik/traefik/pull/9534) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[tls,service]** Enforce default servers transport SPIFFE config ([#9444](https://github.com/traefik/traefik/pull/9444) by [jlevesy](https://github.com/jlevesy))
|
||||
|
||||
**Documentation:**
|
||||
- **[metrics]** Update and publish official Grafana Dashboard ([#9493](https://github.com/traefik/traefik/pull/9493) by [mloiseleur](https://github.com/mloiseleur))
|
||||
|
||||
**Misc:**
|
||||
- Merge branch v2.9 into master ([#9554](https://github.com/traefik/traefik/pull/9554) by [ldez](https://github.com/ldez))
|
||||
- Merge branch v2.9 into master ([#9536](https://github.com/traefik/traefik/pull/9536) by [ldez](https://github.com/ldez))
|
||||
- Merge branch v2.9 into master ([#9532](https://github.com/traefik/traefik/pull/9532) by [ldez](https://github.com/ldez))
|
||||
- Merge branch v2.9 into master ([#9482](https://github.com/traefik/traefik/pull/9482) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.9 into master ([#9464](https://github.com/traefik/traefik/pull/9464) by [ldez](https://github.com/ldez))
|
||||
- Merge branch v2.9 into master ([#9449](https://github.com/traefik/traefik/pull/9449) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.9 into master ([#9419](https://github.com/traefik/traefik/pull/9419) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.9 into master ([#9351](https://github.com/traefik/traefik/pull/9351) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v2.9.5](https://github.com/traefik/traefik/tree/v2.9.5) (2022-11-17)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.4...v2.9.5)
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
|||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Websocket, HTTP/2, gRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- Fast
|
||||
|
|
|
@ -79,7 +79,7 @@ Complete documentation is available at https://traefik.io`,
|
|||
|
||||
err = cli.Execute(cmdTraefik)
|
||||
if err != nil {
|
||||
stdlog.Println(err)
|
||||
log.Error().Err(err).Msg("Command error")
|
||||
logrus.Exit(1)
|
||||
}
|
||||
|
||||
|
@ -504,24 +504,30 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
|||
logger := log.With().Str(logs.MetricsProviderName, "datadog").Logger()
|
||||
|
||||
registries = append(registries, metrics.RegisterDatadog(logger.WithContext(context.Background()), metricsConfig.Datadog))
|
||||
logger.Debug().Msgf("Configured Datadog metrics: pushing to %s once every %s",
|
||||
metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.Datadog.Address).
|
||||
Str("pushInterval", metricsConfig.Datadog.PushInterval.String()).
|
||||
Msgf("Configured Datadog metrics")
|
||||
}
|
||||
|
||||
if metricsConfig.StatsD != nil {
|
||||
logger := log.With().Str(logs.MetricsProviderName, "statsd").Logger()
|
||||
|
||||
registries = append(registries, metrics.RegisterStatsd(logger.WithContext(context.Background()), metricsConfig.StatsD))
|
||||
logger.Debug().Msgf("Configured StatsD metrics: pushing to %s once every %s",
|
||||
metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.StatsD.Address).
|
||||
Str("pushInterval", metricsConfig.StatsD.PushInterval.String()).
|
||||
Msg("Configured StatsD metrics")
|
||||
}
|
||||
|
||||
if metricsConfig.InfluxDB != nil {
|
||||
logger := log.With().Str(logs.MetricsProviderName, "influxdb").Logger()
|
||||
|
||||
registries = append(registries, metrics.RegisterInfluxDB(logger.WithContext(context.Background()), metricsConfig.InfluxDB))
|
||||
logger.Debug().Msgf("Configured InfluxDB metrics: pushing to %s once every %s",
|
||||
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.InfluxDB.Address).
|
||||
Str("pushInterval", metricsConfig.InfluxDB.PushInterval.String()).
|
||||
Msg("Configured InfluxDB metrics")
|
||||
}
|
||||
|
||||
if metricsConfig.InfluxDB2 != nil {
|
||||
|
@ -530,8 +536,25 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
|||
influxDB2Register := metrics.RegisterInfluxDB2(logger.WithContext(context.Background()), metricsConfig.InfluxDB2)
|
||||
if influxDB2Register != nil {
|
||||
registries = append(registries, influxDB2Register)
|
||||
logger.Debug().Msgf("Configured InfluxDB v2 metrics: pushing to %s (%s org/%s bucket) once every %s",
|
||||
metricsConfig.InfluxDB2.Address, metricsConfig.InfluxDB2.Org, metricsConfig.InfluxDB2.Bucket, metricsConfig.InfluxDB2.PushInterval)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.InfluxDB2.Address).
|
||||
Str("bucket", metricsConfig.InfluxDB2.Bucket).
|
||||
Str("organization", metricsConfig.InfluxDB2.Org).
|
||||
Str("pushInterval", metricsConfig.InfluxDB2.PushInterval.String()).
|
||||
Msg("Configured InfluxDB v2 metrics")
|
||||
}
|
||||
}
|
||||
|
||||
if metricsConfig.OpenTelemetry != nil {
|
||||
logger := log.With().Str(logs.MetricsProviderName, "openTelemetry").Logger()
|
||||
|
||||
openTelemetryRegistry := metrics.RegisterOpenTelemetry(logger.WithContext(context.Background()), metricsConfig.OpenTelemetry)
|
||||
if openTelemetryRegistry != nil {
|
||||
registries = append(registries, openTelemetryRegistry)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.OpenTelemetry.Address).
|
||||
Str("pushInterval", metricsConfig.OpenTelemetry.PushInterval.String()).
|
||||
Msg("Configured OpenTelemetry metrics")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -617,6 +640,14 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
|
|||
}
|
||||
}
|
||||
|
||||
if conf.OpenTelemetry != nil {
|
||||
if backend != nil {
|
||||
log.Error().Msg("Tracing backends are all mutually exclusive: cannot create OpenTelemetry backend.")
|
||||
} else {
|
||||
backend = conf.OpenTelemetry
|
||||
}
|
||||
}
|
||||
|
||||
if backend == nil {
|
||||
log.Debug().Msg("Could not initialize tracing, using Jaeger by default")
|
||||
defaultBackend := &jaeger.Config{}
|
||||
|
|
|
@ -79,7 +79,7 @@ traefik --help
|
|||
# or
|
||||
|
||||
docker run traefik[:version] --help
|
||||
# ex: docker run traefik:v2.9 --help
|
||||
# ex: docker run traefik:v3.0 --help
|
||||
```
|
||||
|
||||
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||
|
|
|
@ -21,7 +21,7 @@ Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and
|
|||
|
||||
```bash
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v2.9
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.0
|
||||
```
|
||||
|
||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||
|
@ -29,7 +29,7 @@ For more details, go to the [Docker provider documentation](../providers/docker.
|
|||
!!! tip
|
||||
|
||||
* Prefer a fixed version than the latest that could be an unexpected version.
|
||||
ex: `traefik:v2.9`
|
||||
ex: `traefik:v3.0`
|
||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||
* Any orchestrator using docker images can fetch the official Traefik docker image.
|
||||
|
||||
|
@ -44,10 +44,10 @@ Traefik can be installed in Kubernetes using the Helm chart from <https://github
|
|||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.14+
|
||||
* Helm version 3.x is [installed](https://helm.sh/docs/intro/install/)
|
||||
* Kubernetes 1.16+
|
||||
* Helm version 3.9+ is [installed](https://helm.sh/docs/intro/install/)
|
||||
|
||||
Add Traefik's chart repository to Helm:
|
||||
Add Traefik Labs chart repository to Helm:
|
||||
|
||||
```bash
|
||||
helm repo add traefik https://traefik.github.io/charts
|
||||
|
@ -68,6 +68,9 @@ helm install traefik traefik/traefik
|
|||
!!! tip "Helm Features"
|
||||
|
||||
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
||||
|
||||
Examples are provided [here](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md).
|
||||
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
```bash tab="Install in a Dedicated Namespace"
|
||||
|
@ -83,8 +86,7 @@ helm install traefik traefik/traefik
|
|||
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
||||
{: #helm-custom-values }
|
||||
|
||||
The values are not (yet) documented, but are self-explanatory:
|
||||
you can look at the [default `values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml) file to explore possibilities.
|
||||
All parameters are documented in the default [`values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml).
|
||||
|
||||
You can also set Traefik command line flags using `additionalArguments`.
|
||||
Example of installation with logging set to `DEBUG`:
|
||||
|
|
|
@ -130,7 +130,7 @@ spec:
|
|||
serviceAccountName: traefik-account
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --api.insecure
|
||||
- --providers.kubernetesingress
|
||||
|
|
|
@ -20,7 +20,7 @@ version: '3'
|
|||
services:
|
||||
reverse-proxy:
|
||||
# The official v2 Traefik docker image
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
# Enables the web UI and tells Traefik to listen to docker
|
||||
command: --api.insecure=true --providers.docker
|
||||
ports:
|
||||
|
@ -50,7 +50,12 @@ Now that we have a Traefik instance up and running, we will deploy new services.
|
|||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
...
|
||||
|
||||
whoami:
|
||||
# A container that exposes an API to show its IP address
|
||||
image: traefik/whoami
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Traefik ContentType Documentation"
|
||||
description: "Traefik Proxy's HTTP middleware can automatically specify the content-type header if it has not been defined by the backend. Read the technical documentation."
|
||||
description: "Traefik Proxy's HTTP middleware automatically sets the `Content-Type` header value when it is not set by the backend. Read the technical documentation."
|
||||
---
|
||||
|
||||
# ContentType
|
||||
|
@ -8,84 +8,59 @@ description: "Traefik Proxy's HTTP middleware can automatically specify the cont
|
|||
Handling Content-Type auto-detection
|
||||
{: .subtitle }
|
||||
|
||||
The Content-Type middleware - or rather its `autoDetect` option -
|
||||
specifies whether to let the `Content-Type` header,
|
||||
if it has not been defined by the backend,
|
||||
be automatically set to a value derived from the contents of the response.
|
||||
|
||||
As a proxy, the default behavior should be to leave the header alone,
|
||||
regardless of what the backend did with it.
|
||||
However, the historic default was to always auto-detect and set the header if it was not already defined,
|
||||
and altering this behavior would be a breaking change which would impact many users.
|
||||
|
||||
This middleware exists to enable the correct behavior until at least the default one can be changed in a future version.
|
||||
The Content-Type middleware sets the `Content-Type` header value to the media type detected from the response content,
|
||||
when it is not set by the backend.
|
||||
|
||||
!!! info
|
||||
|
||||
As explained above, for compatibility reasons the default behavior on a router (without this middleware),
|
||||
is still to automatically set the `Content-Type` header.
|
||||
Therefore, given the default value of the `autoDetect` option (false),
|
||||
simply enabling this middleware for a router switches the router's behavior.
|
||||
|
||||
The scope of the Content-Type middleware is the MIME type detection done by the core of Traefik (the server part).
|
||||
Therefore, it has no effect against any other `Content-Type` header modifications (e.g.: in another middleware such as compress).
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker"
|
||||
# Disable auto-detection
|
||||
# Enable auto-detection
|
||||
labels:
|
||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
||||
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Disable auto-detection
|
||||
# Enable auto-detection
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: autodetect
|
||||
spec:
|
||||
contentType:
|
||||
autoDetect: false
|
||||
contentType: {}
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Disable auto-detection
|
||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
||||
# Enable auto-detection
|
||||
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
"labels": {
|
||||
"traefik.http.middlewares.autodetect.contenttype.autodetect": "false"
|
||||
"traefik.http.middlewares.autodetect.contenttype": "true"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
# Disable auto-detection
|
||||
# Enable auto-detection
|
||||
labels:
|
||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
||||
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Disable auto-detection
|
||||
# Enable auto-detection
|
||||
http:
|
||||
middlewares:
|
||||
autodetect:
|
||||
contentType:
|
||||
autoDetect: false
|
||||
contentType: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Disable auto-detection
|
||||
# Enable auto-detection
|
||||
[http.middlewares]
|
||||
[http.middlewares.autodetect.contentType]
|
||||
autoDetect=false
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### `autoDetect`
|
||||
|
||||
`autoDetect` specifies whether to let the `Content-Type` header,
|
||||
if it has not been set by the backend,
|
||||
be automatically set to a value derived from the contents of the response.
|
||||
```
|
|
@ -30,3 +30,28 @@ In v3, the reported status code for gRPC requests is now the value of the `Grpc-
|
|||
- `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy` options of the Headers middleware have been removed.
|
||||
- The `forceSlash` option of the StripPrefix middleware has been removed.
|
||||
- the `preferServerCipherSuites` option has been removed.
|
||||
|
||||
## Matchers
|
||||
|
||||
In v3, the `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `HeaderRegexp` respectively.
|
||||
|
||||
`QueryRegexp` has been introduced to match query values using a regular expression.
|
||||
|
||||
`HeaderRegexp`, `HostRegexp`, `PathRegexp`, `QueryRegexp`, and `HostSNIRegexp` matchers now uses the [Go regexp syntax](https://golang.org/pkg/regexp/syntax/).
|
||||
|
||||
All matchers now take a single value (except `Headers`, `HeaderRegexp`, `Query`, and `QueryRegexp` which take two)
|
||||
and should be explicitly combined using logical operators to mimic previous behavior.
|
||||
|
||||
`Query` can take a single value to match is the query value that has no value (e.g. `/search?mobile`).
|
||||
|
||||
`HostHeader` has been removed, use `Host` instead.
|
||||
|
||||
## Content-Type Auto-Detection
|
||||
|
||||
In v3, the `Content-Type` header is not auto-detected anymore when it is not set by the backend.
|
||||
One should use the `ContentType` middleware to enable the `Content-Type` header value auto-detection.
|
||||
|
||||
## HTTP/3
|
||||
|
||||
In v3, HTTP/3 is no longer an experimental feature.
|
||||
The `experimental.http3` option has been removed from the static configuration.
|
||||
|
|
|
@ -255,7 +255,7 @@ version: "3.7"
|
|||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
environment:
|
||||
- TZ=US/Alaska
|
||||
command:
|
||||
|
|
353
docs/content/observability/metrics/opentelemetry.md
Normal file
353
docs/content/observability/metrics/opentelemetry.md
Normal file
|
@ -0,0 +1,353 @@
|
|||
---
|
||||
title: "Traefik OpenTelemetry Documentation"
|
||||
description: "Traefik supports several metrics backends, including OpenTelemetry. Learn how to implement it for observability in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# OpenTelemetry
|
||||
|
||||
To enable the OpenTelemetry:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry=true
|
||||
```
|
||||
|
||||
!!! info "The OpenTelemetry exporter will export metrics to the collector by using HTTP by default, see the [gRPC Section](#grpc-configuration) to use gRPC."
|
||||
|
||||
#### `address`
|
||||
|
||||
_Required, Default="localhost:4318", Format="`<host>:<port>`"_
|
||||
|
||||
Address of the OpenTelemetry Collector to send metrics to.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
address: localhost:4318
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
address = "localhost:4318"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.address=localhost:4318
|
||||
```
|
||||
|
||||
#### `addEntryPointsLabels`
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
Enable metrics on entry points.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
addEntryPointsLabels: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
addEntryPointsLabels = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.addEntryPointsLabels=true
|
||||
```
|
||||
|
||||
#### `addRoutersLabels`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
Enable metrics on routers.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
addRoutersLabels: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
addRoutersLabels = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.addRoutersLabels=true
|
||||
```
|
||||
|
||||
#### `addServicesLabels`
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
Enable metrics on services.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
addServicesLabels: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
addServicesLabels = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.addServicesLabels=true
|
||||
```
|
||||
|
||||
#### `explicitBoundaries`
|
||||
|
||||
_Optional, Default=".005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10"_
|
||||
|
||||
Explicit boundaries for Histogram data points.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
explicitBoundaries:
|
||||
- 0.1
|
||||
- 0.3
|
||||
- 1.2
|
||||
- 5.0
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
explicitBoundaries = [0.1,0.3,1.2,5.0]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.explicitBoundaries=0.1,0.3,1.2,5.0
|
||||
```
|
||||
|
||||
#### `headers`
|
||||
|
||||
_Optional, Default={}_
|
||||
|
||||
Additional headers sent with metrics by the reporter to the OpenTelemetry Collector.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
headers:
|
||||
foo: bar
|
||||
baz: buz
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry.headers]
|
||||
foo = "bar"
|
||||
baz = "buz"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.headers.foo=bar --metrics.openTelemetry.headers.baz=buz
|
||||
```
|
||||
|
||||
#### `insecure`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
insecure: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
insecure = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.insecure=true
|
||||
```
|
||||
|
||||
#### `pushInterval`
|
||||
|
||||
_Optional, Default=10s_
|
||||
|
||||
Interval at which metrics are sent to the OpenTelemetry Collector.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
pushInterval: 10s
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
pushInterval = "10s"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.pushInterval=10s
|
||||
```
|
||||
|
||||
#### `path`
|
||||
|
||||
_Required, Default="/v1/traces"_
|
||||
|
||||
Allows to override the default URL path used for sending metrics.
|
||||
This option has no effect when using gRPC transport.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
path: /foo/v1/traces
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry]
|
||||
path = "/foo/v1/traces"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.path=/foo/v1/traces
|
||||
```
|
||||
|
||||
#### `tls`
|
||||
|
||||
_Optional_
|
||||
|
||||
Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector.
|
||||
|
||||
##### `ca`
|
||||
|
||||
_Optional_
|
||||
|
||||
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
|
||||
it defaults to the system bundle.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
tls:
|
||||
ca: path/to/ca.crt
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics.openTelemetry.tls]
|
||||
ca = "path/to/ca.crt"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.tls.ca=path/to/ca.crt
|
||||
```
|
||||
|
||||
##### `cert`
|
||||
|
||||
_Optional_
|
||||
|
||||
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
|
||||
When using this option, setting the `key` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics.openTelemetry.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.tls.cert=path/to/foo.cert
|
||||
--metrics.openTelemetry.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `key`
|
||||
|
||||
_Optional_
|
||||
|
||||
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
|
||||
When using this option, setting the `cert` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics.openTelemetry.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.tls.cert=path/to/foo.cert
|
||||
--metrics.openTelemetry.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `insecureSkipVerify`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
If `insecureSkipVerify` is `true`,
|
||||
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics.openTelemetry.tls]
|
||||
insecureSkipVerify = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.tls.insecureSkipVerify=true
|
||||
```
|
||||
|
||||
#### gRPC configuration
|
||||
|
||||
This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
openTelemetry:
|
||||
grpc: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.openTelemetry.grpc]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.openTelemetry.grpc=true
|
||||
```
|
246
docs/content/observability/tracing/opentelemetry.md
Normal file
246
docs/content/observability/tracing/opentelemetry.md
Normal file
|
@ -0,0 +1,246 @@
|
|||
---
|
||||
title: "Traefik OpenTelemetry Documentation"
|
||||
description: "Traefik supports several tracing backends, including OpenTelemetry. Learn how to implement it for observability in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# OpenTelemetry
|
||||
|
||||
To enable the OpenTelemetry tracer:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry=true
|
||||
```
|
||||
|
||||
!!! info "The OpenTelemetry trace reporter will export traces to the collector using HTTP by default, see the [gRPC Section](#grpc-configuration) to use gRPC."
|
||||
|
||||
!!! info "Trace sampling"
|
||||
|
||||
By default, the OpenTelemetry trace reporter will sample 100% of traces.
|
||||
See [OpenTelemetry's SDK configuration](https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/#general-sdk-configuration) to customize the sampling strategy.
|
||||
|
||||
#### `address`
|
||||
|
||||
_Required, Default="localhost:4318", Format="`<host>:<port>`"_
|
||||
|
||||
Address of the OpenTelemetry Collector to send spans to.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
address: localhost:4318
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry]
|
||||
address = "localhost:4318"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.address=localhost:4318
|
||||
```
|
||||
|
||||
#### `headers`
|
||||
|
||||
_Optional, Default={}_
|
||||
|
||||
Additional headers sent with spans by the reporter to the OpenTelemetry Collector.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
headers:
|
||||
foo: bar
|
||||
baz: buz
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry.headers]
|
||||
foo = "bar"
|
||||
baz = "buz"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.headers.foo=bar --tracing.openTelemetry.headers.baz=buz
|
||||
```
|
||||
|
||||
#### `insecure`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
Allows reporter to send spans to the OpenTelemetry Collector without using a secured protocol.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
insecure: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry]
|
||||
insecure = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.insecure=true
|
||||
```
|
||||
|
||||
#### `path`
|
||||
|
||||
_Required, Default="/v1/traces"_
|
||||
|
||||
Allows to override the default URL path used for sending traces.
|
||||
This option has no effect when using gRPC transport.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
path: /foo/v1/traces
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry]
|
||||
path = "/foo/v1/traces"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.path=/foo/v1/traces
|
||||
```
|
||||
|
||||
#### `tls`
|
||||
|
||||
_Optional_
|
||||
|
||||
Defines the TLS configuration used by the reporter to send spans to the OpenTelemetry Collector.
|
||||
|
||||
##### `ca`
|
||||
|
||||
_Optional_
|
||||
|
||||
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
|
||||
it defaults to the system bundle.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
tls:
|
||||
ca: path/to/ca.crt
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing.openTelemetry.tls]
|
||||
ca = "path/to/ca.crt"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.tls.ca=path/to/ca.crt
|
||||
```
|
||||
|
||||
##### `cert`
|
||||
|
||||
_Optional_
|
||||
|
||||
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
|
||||
When using this option, setting the `key` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing.openTelemetry.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.tls.cert=path/to/foo.cert
|
||||
--tracing.openTelemetry.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `key`
|
||||
|
||||
_Optional_
|
||||
|
||||
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
|
||||
When using this option, setting the `cert` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing.openTelemetry.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.tls.cert=path/to/foo.cert
|
||||
--tracing.openTelemetry.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `insecureSkipVerify`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
If `insecureSkipVerify` is `true`,
|
||||
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing.openTelemetry.tls]
|
||||
insecureSkipVerify = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.tls.insecureSkipVerify=true
|
||||
```
|
||||
|
||||
#### gRPC configuration
|
||||
|
||||
_Optional_
|
||||
|
||||
This instructs the reporter to send spans to the OpenTelemetry Collector using gRPC.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
tracing:
|
||||
openTelemetry:
|
||||
grpc: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[tracing]
|
||||
[tracing.openTelemetry.grpc]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.openTelemetry.grpc=true
|
||||
```
|
|
@ -93,12 +93,12 @@ rule = "Host(`traefik.example.com`)"
|
|||
|
||||
```bash tab="Path Prefix Rule"
|
||||
# The dashboard can be accessed on http://example.com/dashboard/ or http://traefik.example.com/dashboard/
|
||||
rule = "PathPrefix(`/api`, `/dashboard`)"
|
||||
rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
||||
```
|
||||
|
||||
```bash tab="Combination of Rules"
|
||||
# The dashboard can be accessed on http://traefik.example.com/dashboard/
|
||||
rule = "Host(`traefik.example.com`) && PathPrefix(`/api`, `/dashboard`)"
|
||||
rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||
```
|
||||
|
||||
??? example "Dashboard Dynamic Configuration Examples"
|
||||
|
|
|
@ -265,7 +265,7 @@ See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API A
|
|||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.9 # The official v2 Traefik docker image
|
||||
image: traefik:v3.0 # The official v2 Traefik docker image
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
- "traefik.http.middlewares.middleware05.compress=true"
|
||||
- "traefik.http.middlewares.middleware05.compress.excludedcontenttypes=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware05.compress.minresponsebodybytes=42"
|
||||
- "traefik.http.middlewares.middleware06.contenttype.autodetect=true"
|
||||
- "traefik.http.middlewares.middleware06.contenttype=true"
|
||||
- "traefik.http.middlewares.middleware07.digestauth.headerfield=foobar"
|
||||
- "traefik.http.middlewares.middleware07.digestauth.realm=foobar"
|
||||
- "traefik.http.middlewares.middleware07.digestauth.removeheader=true"
|
||||
|
|
|
@ -137,7 +137,6 @@
|
|||
minResponseBodyBytes = 42
|
||||
[http.middlewares.Middleware06]
|
||||
[http.middlewares.Middleware06.contentType]
|
||||
autoDetect = true
|
||||
[http.middlewares.Middleware07]
|
||||
[http.middlewares.Middleware07.digestAuth]
|
||||
users = ["foobar", "foobar"]
|
||||
|
|
|
@ -141,8 +141,7 @@ http:
|
|||
- foobar
|
||||
minResponseBodyBytes: 42
|
||||
Middleware06:
|
||||
contentType:
|
||||
autoDetect: true
|
||||
contentType: {}
|
||||
Middleware07:
|
||||
digestAuth:
|
||||
users:
|
||||
|
|
|
@ -39,7 +39,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -56,11 +56,11 @@ spec:
|
|||
- Rule
|
||||
type: string
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule'
|
||||
type: string
|
||||
middlewares:
|
||||
description: 'Middlewares defines the list of references to
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
|
@ -79,7 +79,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of Service. It can contain
|
||||
|
@ -145,7 +145,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -190,16 +190,16 @@ spec:
|
|||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls'
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -217,15 +217,15 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSOption.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -241,11 +241,11 @@ spec:
|
|||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSStore.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -307,7 +307,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -318,7 +318,7 @@ spec:
|
|||
description: RouteTCP holds the TCP route configuration.
|
||||
properties:
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule_1'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1'
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to MiddlewareTCP
|
||||
|
@ -341,7 +341,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority_1'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of TCP services.
|
||||
|
@ -366,7 +366,7 @@ spec:
|
|||
x-kubernetes-int-or-string: true
|
||||
proxyProtocol:
|
||||
description: 'ProxyProtocol defines the PROXY protocol
|
||||
configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol'
|
||||
configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol'
|
||||
properties:
|
||||
version:
|
||||
description: Version defines the PROXY Protocol version
|
||||
|
@ -397,16 +397,16 @@ spec:
|
|||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration on a layer 4 / TCP
|
||||
Route. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls_1'
|
||||
Route. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -424,7 +424,7 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Traefik
|
||||
|
@ -518,7 +518,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -597,7 +597,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'Middleware is the CRD implementation of a Traefik Middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -617,7 +617,7 @@ spec:
|
|||
addPrefix:
|
||||
description: 'AddPrefix holds the add prefix middleware configuration.
|
||||
This middleware updates the path of a request before forwarding
|
||||
it. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/addprefix/'
|
||||
it. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/'
|
||||
properties:
|
||||
prefix:
|
||||
description: Prefix is the string to add before the current path
|
||||
|
@ -627,11 +627,11 @@ spec:
|
|||
basicAuth:
|
||||
description: 'BasicAuth holds the basic auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -651,7 +651,7 @@ spec:
|
|||
buffering:
|
||||
description: 'Buffering holds the buffering middleware configuration.
|
||||
This middleware retries or limits the size of requests that can
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
properties:
|
||||
maxRequestBodyBytes:
|
||||
description: 'MaxRequestBodyBytes defines the maximum allowed
|
||||
|
@ -684,13 +684,13 @@ spec:
|
|||
retryExpression:
|
||||
description: 'RetryExpression defines the retry conditions. It
|
||||
is a logical combination of functions with operators AND (&&)
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#retryexpression'
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression'
|
||||
type: string
|
||||
type: object
|
||||
chain:
|
||||
description: 'Chain holds the configuration of the chain middleware.
|
||||
This middleware enables to define reusable combinations of other
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/chain/'
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/'
|
||||
properties:
|
||||
middlewares:
|
||||
description: Middlewares is the list of MiddlewareRef which composes
|
||||
|
@ -744,7 +744,7 @@ spec:
|
|||
compress:
|
||||
description: 'Compress holds the compress middleware configuration.
|
||||
This middleware compresses responses before sending them to the
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/compress/'
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/'
|
||||
properties:
|
||||
excludedContentTypes:
|
||||
description: ExcludedContentTypes defines the list of content
|
||||
|
@ -762,28 +762,18 @@ spec:
|
|||
type: object
|
||||
contentType:
|
||||
description: ContentType holds the content-type middleware configuration.
|
||||
This middleware exists to enable the correct behavior until at least
|
||||
the default one can be changed in a future version.
|
||||
properties:
|
||||
autoDetect:
|
||||
description: AutoDetect specifies whether to let the `Content-Type`
|
||||
header, if it has not been set by the backend, be automatically
|
||||
set to a value derived from the contents of the response. As
|
||||
a proxy, the default behavior should be to leave the header
|
||||
alone, regardless of what the backend did with it. However,
|
||||
the historic default was to always auto-detect and set the header
|
||||
if it was nil, and it is going to be kept that way in order
|
||||
to support users currently relying on it.
|
||||
type: boolean
|
||||
This middleware sets the `Content-Type` header value to the media
|
||||
type detected from the response content, when it is not set by the
|
||||
backend.
|
||||
type: object
|
||||
digestAuth:
|
||||
description: 'DigestAuth holds the digest auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/digestauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -802,7 +792,7 @@ spec:
|
|||
errors:
|
||||
description: 'ErrorPage holds the custom error middleware configuration.
|
||||
This middleware returns a custom page in lieu of the default, according
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/'
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/'
|
||||
properties:
|
||||
query:
|
||||
description: Query defines the URL for the error page (hosted
|
||||
|
@ -811,7 +801,7 @@ spec:
|
|||
type: string
|
||||
service:
|
||||
description: 'Service defines the reference to a Kubernetes Service
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/#service'
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service'
|
||||
properties:
|
||||
kind:
|
||||
description: Kind defines the kind of the Service.
|
||||
|
@ -868,7 +858,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -917,7 +907,7 @@ spec:
|
|||
forwardAuth:
|
||||
description: 'ForwardAuth holds the forward auth middleware configuration.
|
||||
This middleware delegates the request authentication to a Service.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
|
||||
properties:
|
||||
address:
|
||||
description: Address defines the authentication server address.
|
||||
|
@ -940,7 +930,7 @@ spec:
|
|||
description: 'AuthResponseHeadersRegex defines the regex to match
|
||||
headers to copy from the authentication server response and
|
||||
set on forwarded request, after stripping all headers that match
|
||||
the regex. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
the regex. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
type: string
|
||||
tls:
|
||||
description: TLS defines the configuration used to secure the
|
||||
|
@ -980,7 +970,7 @@ spec:
|
|||
headers:
|
||||
description: 'Headers holds the headers middleware configuration.
|
||||
This middleware manages the requests and responses headers. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/headers/#customrequestheaders'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders'
|
||||
properties:
|
||||
accessControlAllowCredentials:
|
||||
description: AccessControlAllowCredentials defines whether the
|
||||
|
@ -1124,7 +1114,7 @@ spec:
|
|||
inFlightReq:
|
||||
description: 'InFlightReq holds the in-flight request middleware configuration.
|
||||
This middleware limits the number of requests being processed and
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/'
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/'
|
||||
properties:
|
||||
amount:
|
||||
description: Amount defines the maximum amount of allowed simultaneous
|
||||
|
@ -1138,11 +1128,11 @@ spec:
|
|||
group requests as originating from a common source. If several
|
||||
strategies are defined at the same time, an error will be raised.
|
||||
If none are set, the default is to use the requestHost. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/#sourcecriterion'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1170,11 +1160,11 @@ spec:
|
|||
ipAllowList:
|
||||
description: 'IPAllowList holds the IP allowlist middleware configuration.
|
||||
This middleware accepts / refuses requests based on the client IP.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration used
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1198,7 +1188,7 @@ spec:
|
|||
passTLSClientCert:
|
||||
description: 'PassTLSClientCert holds the pass TLS client cert middleware
|
||||
configuration. This middleware adds the selected data from the passed
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/passtlsclientcert/'
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/'
|
||||
properties:
|
||||
info:
|
||||
description: Info selects the specific client certificate details
|
||||
|
@ -1305,7 +1295,7 @@ spec:
|
|||
rateLimit:
|
||||
description: 'RateLimit holds the rate limit configuration. This middleware
|
||||
ensures that services will receive a fair amount of requests, and
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ratelimit/'
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/'
|
||||
properties:
|
||||
average:
|
||||
description: Average is the maximum rate, by default in requests/s,
|
||||
|
@ -1338,7 +1328,7 @@ spec:
|
|||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1366,7 +1356,7 @@ spec:
|
|||
redirectRegex:
|
||||
description: 'RedirectRegex holds the redirect regex middleware configuration.
|
||||
This middleware redirects a request using regex matching and replacement.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectregex/#regex'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -1384,7 +1374,7 @@ spec:
|
|||
redirectScheme:
|
||||
description: 'RedirectScheme holds the redirect scheme middleware
|
||||
configuration. This middleware redirects requests from a scheme/port
|
||||
to another. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectscheme/'
|
||||
to another. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -1400,7 +1390,7 @@ spec:
|
|||
replacePath:
|
||||
description: 'ReplacePath holds the replace path middleware configuration.
|
||||
This middleware replaces the path of the request URL and store the
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepath/'
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/'
|
||||
properties:
|
||||
path:
|
||||
description: Path defines the path to use as replacement in the
|
||||
|
@ -1410,7 +1400,7 @@ spec:
|
|||
replacePathRegex:
|
||||
description: 'ReplacePathRegex holds the replace path regex middleware
|
||||
configuration. This middleware replaces the path of a URL using
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepathregex/'
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression used to match
|
||||
|
@ -1426,7 +1416,7 @@ spec:
|
|||
middleware reissues requests a given number of times to a backend
|
||||
server if that server does not reply. As soon as the server answers,
|
||||
the middleware stops retrying, regardless of the response status.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/retry/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/'
|
||||
properties:
|
||||
attempts:
|
||||
description: Attempts defines how many times the request should
|
||||
|
@ -1446,7 +1436,7 @@ spec:
|
|||
stripPrefix:
|
||||
description: 'StripPrefix holds the strip prefix middleware configuration.
|
||||
This middleware removes the specified prefixes from the URL path.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefix/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/'
|
||||
properties:
|
||||
prefixes:
|
||||
description: Prefixes defines the prefixes to strip from the request
|
||||
|
@ -1458,7 +1448,7 @@ spec:
|
|||
stripPrefixRegex:
|
||||
description: 'StripPrefixRegex holds the strip prefix regex middleware
|
||||
configuration. This middleware removes the matching prefixes from
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefixregex/'
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression to match the
|
||||
|
@ -1502,7 +1492,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'MiddlewareTCP is the CRD implementation of a Traefik TCP middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1576,7 +1566,7 @@ spec:
|
|||
description: 'ServersTransport is the CRD implementation of a ServersTransport.
|
||||
If no serversTransport is specified, the default@internal will be used.
|
||||
The default@internal serversTransport is created from the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#serverstransport_1'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_1'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1716,7 +1706,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TLSOption is the CRD implementation of a Traefik TLS Option,
|
||||
allowing to configure some parameters of the TLS connection. More info:
|
||||
https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1736,13 +1726,13 @@ spec:
|
|||
alpnProtocols:
|
||||
description: 'ALPNProtocols defines the list of supported application
|
||||
level protocols for the TLS handshake, in order of preference. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/https/tls/#alpn-protocols'
|
||||
info: https://doc.traefik.io/traefik/v3.0/https/tls/#alpn-protocols'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cipherSuites:
|
||||
description: 'CipherSuites defines the list of supported cipher suites
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#cipher-suites'
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#cipher-suites'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -1769,7 +1759,7 @@ spec:
|
|||
type: object
|
||||
curvePreferences:
|
||||
description: 'CurvePreferences defines the preferred elliptic curves
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#curve-preferences'
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#curve-preferences'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -1824,7 +1814,7 @@ spec:
|
|||
description: 'TLSStore is the CRD implementation of a Traefik TLS Store. For
|
||||
the time being, only the TLSStore named default is supported. This means
|
||||
that you cannot have two stores that are named default in different Kubernetes
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#certificates-stores'
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#certificates-stores'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1922,7 +1912,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TraefikService is the CRD implementation of a Traefik Service.
|
||||
TraefikService object allows to: - Apply weight to Services on load-balancing
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -2021,7 +2011,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2105,7 +2095,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2205,7 +2195,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2244,7 +2234,7 @@ spec:
|
|||
type: array
|
||||
sticky:
|
||||
description: 'Sticky defines whether sticky sessions are enabled.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
|
|
@ -25,7 +25,7 @@ spec:
|
|||
serviceAccountName: traefik-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --entrypoints.web.address=:80
|
||||
- --entrypoints.websecure.address=:443
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware05/compress/minResponseBodyBytes` | `42` |
|
||||
| `traefik/http/middlewares/Middleware06/contentType/autoDetect` | `true` |
|
||||
| `traefik/http/middlewares/Middleware06/contentType` | `` |
|
||||
| `traefik/http/middlewares/Middleware07/digestAuth/headerField` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware07/digestAuth/realm` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware07/digestAuth/removeHeader` | `true` |
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"traefik.http.middlewares.middleware05.compress": "true",
|
||||
"traefik.http.middlewares.middleware05.compress.excludedcontenttypes": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware05.compress.minresponsebodybytes": "42",
|
||||
"traefik.http.middlewares.middleware06.contenttype.autodetect": "true",
|
||||
"traefik.http.middlewares.middleware06.contenttype": "true",
|
||||
"traefik.http.middlewares.middleware07.digestauth.headerfield": "foobar",
|
||||
"traefik.http.middlewares.middleware07.digestauth.realm": "foobar",
|
||||
"traefik.http.middlewares.middleware07.digestauth.removeheader": "true",
|
||||
|
|
|
@ -39,7 +39,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -56,11 +56,11 @@ spec:
|
|||
- Rule
|
||||
type: string
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule'
|
||||
type: string
|
||||
middlewares:
|
||||
description: 'Middlewares defines the list of references to
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
|
@ -79,7 +79,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of Service. It can contain
|
||||
|
@ -145,7 +145,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -190,16 +190,16 @@ spec:
|
|||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls'
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -217,15 +217,15 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSOption.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -241,11 +241,11 @@ spec:
|
|||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSStore.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
|
|
@ -39,7 +39,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -50,7 +50,7 @@ spec:
|
|||
description: RouteTCP holds the TCP route configuration.
|
||||
properties:
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule_1'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1'
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to MiddlewareTCP
|
||||
|
@ -73,7 +73,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority_1'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of TCP services.
|
||||
|
@ -98,7 +98,7 @@ spec:
|
|||
x-kubernetes-int-or-string: true
|
||||
proxyProtocol:
|
||||
description: 'ProxyProtocol defines the PROXY protocol
|
||||
configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol'
|
||||
configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol'
|
||||
properties:
|
||||
version:
|
||||
description: Version defines the PROXY Protocol version
|
||||
|
@ -129,16 +129,16 @@ spec:
|
|||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration on a layer 4 / TCP
|
||||
Route. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls_1'
|
||||
Route. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -156,7 +156,7 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Traefik
|
||||
|
|
|
@ -39,7 +39,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
|
|
@ -20,7 +20,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'Middleware is the CRD implementation of a Traefik Middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -40,7 +40,7 @@ spec:
|
|||
addPrefix:
|
||||
description: 'AddPrefix holds the add prefix middleware configuration.
|
||||
This middleware updates the path of a request before forwarding
|
||||
it. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/addprefix/'
|
||||
it. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/'
|
||||
properties:
|
||||
prefix:
|
||||
description: Prefix is the string to add before the current path
|
||||
|
@ -50,11 +50,11 @@ spec:
|
|||
basicAuth:
|
||||
description: 'BasicAuth holds the basic auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -74,7 +74,7 @@ spec:
|
|||
buffering:
|
||||
description: 'Buffering holds the buffering middleware configuration.
|
||||
This middleware retries or limits the size of requests that can
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
properties:
|
||||
maxRequestBodyBytes:
|
||||
description: 'MaxRequestBodyBytes defines the maximum allowed
|
||||
|
@ -107,13 +107,13 @@ spec:
|
|||
retryExpression:
|
||||
description: 'RetryExpression defines the retry conditions. It
|
||||
is a logical combination of functions with operators AND (&&)
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#retryexpression'
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression'
|
||||
type: string
|
||||
type: object
|
||||
chain:
|
||||
description: 'Chain holds the configuration of the chain middleware.
|
||||
This middleware enables to define reusable combinations of other
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/chain/'
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/'
|
||||
properties:
|
||||
middlewares:
|
||||
description: Middlewares is the list of MiddlewareRef which composes
|
||||
|
@ -167,7 +167,7 @@ spec:
|
|||
compress:
|
||||
description: 'Compress holds the compress middleware configuration.
|
||||
This middleware compresses responses before sending them to the
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/compress/'
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/'
|
||||
properties:
|
||||
excludedContentTypes:
|
||||
description: ExcludedContentTypes defines the list of content
|
||||
|
@ -185,28 +185,18 @@ spec:
|
|||
type: object
|
||||
contentType:
|
||||
description: ContentType holds the content-type middleware configuration.
|
||||
This middleware exists to enable the correct behavior until at least
|
||||
the default one can be changed in a future version.
|
||||
properties:
|
||||
autoDetect:
|
||||
description: AutoDetect specifies whether to let the `Content-Type`
|
||||
header, if it has not been set by the backend, be automatically
|
||||
set to a value derived from the contents of the response. As
|
||||
a proxy, the default behavior should be to leave the header
|
||||
alone, regardless of what the backend did with it. However,
|
||||
the historic default was to always auto-detect and set the header
|
||||
if it was nil, and it is going to be kept that way in order
|
||||
to support users currently relying on it.
|
||||
type: boolean
|
||||
This middleware sets the `Content-Type` header value to the media
|
||||
type detected from the response content, when it is not set by the
|
||||
backend.
|
||||
type: object
|
||||
digestAuth:
|
||||
description: 'DigestAuth holds the digest auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/digestauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -225,7 +215,7 @@ spec:
|
|||
errors:
|
||||
description: 'ErrorPage holds the custom error middleware configuration.
|
||||
This middleware returns a custom page in lieu of the default, according
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/'
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/'
|
||||
properties:
|
||||
query:
|
||||
description: Query defines the URL for the error page (hosted
|
||||
|
@ -234,7 +224,7 @@ spec:
|
|||
type: string
|
||||
service:
|
||||
description: 'Service defines the reference to a Kubernetes Service
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/#service'
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service'
|
||||
properties:
|
||||
kind:
|
||||
description: Kind defines the kind of the Service.
|
||||
|
@ -291,7 +281,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -340,7 +330,7 @@ spec:
|
|||
forwardAuth:
|
||||
description: 'ForwardAuth holds the forward auth middleware configuration.
|
||||
This middleware delegates the request authentication to a Service.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
|
||||
properties:
|
||||
address:
|
||||
description: Address defines the authentication server address.
|
||||
|
@ -363,7 +353,7 @@ spec:
|
|||
description: 'AuthResponseHeadersRegex defines the regex to match
|
||||
headers to copy from the authentication server response and
|
||||
set on forwarded request, after stripping all headers that match
|
||||
the regex. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
the regex. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
type: string
|
||||
tls:
|
||||
description: TLS defines the configuration used to secure the
|
||||
|
@ -403,7 +393,7 @@ spec:
|
|||
headers:
|
||||
description: 'Headers holds the headers middleware configuration.
|
||||
This middleware manages the requests and responses headers. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/headers/#customrequestheaders'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders'
|
||||
properties:
|
||||
accessControlAllowCredentials:
|
||||
description: AccessControlAllowCredentials defines whether the
|
||||
|
@ -547,7 +537,7 @@ spec:
|
|||
inFlightReq:
|
||||
description: 'InFlightReq holds the in-flight request middleware configuration.
|
||||
This middleware limits the number of requests being processed and
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/'
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/'
|
||||
properties:
|
||||
amount:
|
||||
description: Amount defines the maximum amount of allowed simultaneous
|
||||
|
@ -561,11 +551,11 @@ spec:
|
|||
group requests as originating from a common source. If several
|
||||
strategies are defined at the same time, an error will be raised.
|
||||
If none are set, the default is to use the requestHost. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/#sourcecriterion'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -593,11 +583,11 @@ spec:
|
|||
ipAllowList:
|
||||
description: 'IPAllowList holds the IP allowlist middleware configuration.
|
||||
This middleware accepts / refuses requests based on the client IP.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration used
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -621,7 +611,7 @@ spec:
|
|||
passTLSClientCert:
|
||||
description: 'PassTLSClientCert holds the pass TLS client cert middleware
|
||||
configuration. This middleware adds the selected data from the passed
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/passtlsclientcert/'
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/'
|
||||
properties:
|
||||
info:
|
||||
description: Info selects the specific client certificate details
|
||||
|
@ -728,7 +718,7 @@ spec:
|
|||
rateLimit:
|
||||
description: 'RateLimit holds the rate limit configuration. This middleware
|
||||
ensures that services will receive a fair amount of requests, and
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ratelimit/'
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/'
|
||||
properties:
|
||||
average:
|
||||
description: Average is the maximum rate, by default in requests/s,
|
||||
|
@ -761,7 +751,7 @@ spec:
|
|||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -789,7 +779,7 @@ spec:
|
|||
redirectRegex:
|
||||
description: 'RedirectRegex holds the redirect regex middleware configuration.
|
||||
This middleware redirects a request using regex matching and replacement.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectregex/#regex'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -807,7 +797,7 @@ spec:
|
|||
redirectScheme:
|
||||
description: 'RedirectScheme holds the redirect scheme middleware
|
||||
configuration. This middleware redirects requests from a scheme/port
|
||||
to another. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectscheme/'
|
||||
to another. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -823,7 +813,7 @@ spec:
|
|||
replacePath:
|
||||
description: 'ReplacePath holds the replace path middleware configuration.
|
||||
This middleware replaces the path of the request URL and store the
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepath/'
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/'
|
||||
properties:
|
||||
path:
|
||||
description: Path defines the path to use as replacement in the
|
||||
|
@ -833,7 +823,7 @@ spec:
|
|||
replacePathRegex:
|
||||
description: 'ReplacePathRegex holds the replace path regex middleware
|
||||
configuration. This middleware replaces the path of a URL using
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepathregex/'
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression used to match
|
||||
|
@ -849,7 +839,7 @@ spec:
|
|||
middleware reissues requests a given number of times to a backend
|
||||
server if that server does not reply. As soon as the server answers,
|
||||
the middleware stops retrying, regardless of the response status.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/retry/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/'
|
||||
properties:
|
||||
attempts:
|
||||
description: Attempts defines how many times the request should
|
||||
|
@ -869,7 +859,7 @@ spec:
|
|||
stripPrefix:
|
||||
description: 'StripPrefix holds the strip prefix middleware configuration.
|
||||
This middleware removes the specified prefixes from the URL path.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefix/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/'
|
||||
properties:
|
||||
prefixes:
|
||||
description: Prefixes defines the prefixes to strip from the request
|
||||
|
@ -881,7 +871,7 @@ spec:
|
|||
stripPrefixRegex:
|
||||
description: 'StripPrefixRegex holds the strip prefix regex middleware
|
||||
configuration. This middleware removes the matching prefixes from
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefixregex/'
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression to match the
|
||||
|
|
|
@ -20,7 +20,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'MiddlewareTCP is the CRD implementation of a Traefik TCP middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
|
|
@ -22,7 +22,7 @@ spec:
|
|||
description: 'ServersTransport is the CRD implementation of a ServersTransport.
|
||||
If no serversTransport is specified, the default@internal will be used.
|
||||
The default@internal serversTransport is created from the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#serverstransport_1'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_1'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
|
|
@ -21,7 +21,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TLSOption is the CRD implementation of a Traefik TLS Option,
|
||||
allowing to configure some parameters of the TLS connection. More info:
|
||||
https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -41,13 +41,13 @@ spec:
|
|||
alpnProtocols:
|
||||
description: 'ALPNProtocols defines the list of supported application
|
||||
level protocols for the TLS handshake, in order of preference. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/https/tls/#alpn-protocols'
|
||||
info: https://doc.traefik.io/traefik/v3.0/https/tls/#alpn-protocols'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cipherSuites:
|
||||
description: 'CipherSuites defines the list of supported cipher suites
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#cipher-suites'
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#cipher-suites'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -74,7 +74,7 @@ spec:
|
|||
type: object
|
||||
curvePreferences:
|
||||
description: 'CurvePreferences defines the preferred elliptic curves
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#curve-preferences'
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#curve-preferences'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
|
|
@ -22,7 +22,7 @@ spec:
|
|||
description: 'TLSStore is the CRD implementation of a Traefik TLS Store. For
|
||||
the time being, only the TLSStore named default is supported. This means
|
||||
that you cannot have two stores that are named default in different Kubernetes
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#certificates-stores'
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#certificates-stores'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
|
|
@ -21,7 +21,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TraefikService is the CRD implementation of a Traefik Service.
|
||||
TraefikService object allows to: - Apply weight to Services on load-balancing
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -120,7 +120,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -204,7 +204,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -304,7 +304,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -343,7 +343,7 @@ spec:
|
|||
type: array
|
||||
sticky:
|
||||
description: 'Sticky defines whether sticky sessions are enabled.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
|
|
@ -189,9 +189,6 @@ WriteTimeout is the maximum duration before timing out writes of the response. I
|
|||
`--entrypoints.<name>.udp.timeout`:
|
||||
Timeout defines how long to wait on an idle session before releasing the related resources. (Default: ```3```)
|
||||
|
||||
`--experimental.http3`:
|
||||
Enable HTTP3. (Default: ```false```)
|
||||
|
||||
`--experimental.hub`:
|
||||
Enable the Traefik Hub provider. (Default: ```false```)
|
||||
|
||||
|
@ -357,6 +354,51 @@ InfluxDB v2 push interval. (Default: ```10```)
|
|||
`--metrics.influxdb2.token`:
|
||||
InfluxDB v2 access token.
|
||||
|
||||
`--metrics.opentelemetry`:
|
||||
OpenTelemetry metrics exporter type. (Default: ```false```)
|
||||
|
||||
`--metrics.opentelemetry.addentrypointslabels`:
|
||||
Enable metrics on entry points. (Default: ```true```)
|
||||
|
||||
`--metrics.opentelemetry.address`:
|
||||
Address (host:port) of the collector endpoint. (Default: ```localhost:4318```)
|
||||
|
||||
`--metrics.opentelemetry.addrouterslabels`:
|
||||
Enable metrics on routers. (Default: ```false```)
|
||||
|
||||
`--metrics.opentelemetry.addserviceslabels`:
|
||||
Enable metrics on services. (Default: ```true```)
|
||||
|
||||
`--metrics.opentelemetry.explicitboundaries`:
|
||||
Boundaries for latency metrics. (Default: ```0.005000, 0.010000, 0.025000, 0.050000, 0.100000, 0.250000, 0.500000, 1.000000, 2.500000, 5.000000, 10.000000```)
|
||||
|
||||
`--metrics.opentelemetry.grpc`:
|
||||
gRPC specific configuration for the OpenTelemetry collector. (Default: ```true```)
|
||||
|
||||
`--metrics.opentelemetry.headers.<name>`:
|
||||
Headers sent with payload.
|
||||
|
||||
`--metrics.opentelemetry.insecure`:
|
||||
Disables client transport security for the exporter. (Default: ```false```)
|
||||
|
||||
`--metrics.opentelemetry.path`:
|
||||
Set the URL path of the collector endpoint.
|
||||
|
||||
`--metrics.opentelemetry.pushinterval`:
|
||||
Period between calls to collect a checkpoint. (Default: ```10```)
|
||||
|
||||
`--metrics.opentelemetry.tls.ca`:
|
||||
TLS CA
|
||||
|
||||
`--metrics.opentelemetry.tls.cert`:
|
||||
TLS cert
|
||||
|
||||
`--metrics.opentelemetry.tls.insecureskipverify`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`--metrics.opentelemetry.tls.key`:
|
||||
TLS key
|
||||
|
||||
`--metrics.prometheus`:
|
||||
Prometheus metrics exporter type. (Default: ```false```)
|
||||
|
||||
|
@ -1095,6 +1137,36 @@ Sets the sampling type. (Default: ```const```)
|
|||
`--tracing.jaeger.tracecontextheadername`:
|
||||
Sets the header name used to store the trace ID. (Default: ```uber-trace-id```)
|
||||
|
||||
`--tracing.opentelemetry`:
|
||||
Settings for OpenTelemetry. (Default: ```false```)
|
||||
|
||||
`--tracing.opentelemetry.address`:
|
||||
Sets the address (host:port) of the collector endpoint. (Default: ```localhost:4318```)
|
||||
|
||||
`--tracing.opentelemetry.grpc`:
|
||||
gRPC specific configuration for the OpenTelemetry collector. (Default: ```true```)
|
||||
|
||||
`--tracing.opentelemetry.headers.<name>`:
|
||||
Defines additional headers to be sent with the payloads.
|
||||
|
||||
`--tracing.opentelemetry.insecure`:
|
||||
Disables client transport security for the exporter. (Default: ```false```)
|
||||
|
||||
`--tracing.opentelemetry.path`:
|
||||
Sets the URL path of the collector endpoint.
|
||||
|
||||
`--tracing.opentelemetry.tls.ca`:
|
||||
TLS CA
|
||||
|
||||
`--tracing.opentelemetry.tls.cert`:
|
||||
TLS cert
|
||||
|
||||
`--tracing.opentelemetry.tls.insecureskipverify`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`--tracing.opentelemetry.tls.key`:
|
||||
TLS key
|
||||
|
||||
`--tracing.servicename`:
|
||||
Set the name for this service. (Default: ```traefik```)
|
||||
|
||||
|
|
|
@ -189,9 +189,6 @@ WriteTimeout is the maximum duration before timing out writes of the response. I
|
|||
`TRAEFIK_ENTRYPOINTS_<NAME>_UDP_TIMEOUT`:
|
||||
Timeout defines how long to wait on an idle session before releasing the related resources. (Default: ```3```)
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_HTTP3`:
|
||||
Enable HTTP3. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_HUB`:
|
||||
Enable the Traefik Hub provider. (Default: ```false```)
|
||||
|
||||
|
@ -357,6 +354,51 @@ InfluxDB retention policy used when protocol is http.
|
|||
`TRAEFIK_METRICS_INFLUXDB_USERNAME`:
|
||||
InfluxDB username (only with http).
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY`:
|
||||
OpenTelemetry metrics exporter type. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_ADDENTRYPOINTSLABELS`:
|
||||
Enable metrics on entry points. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_ADDRESS`:
|
||||
Address (host:port) of the collector endpoint. (Default: ```localhost:4318```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_ADDROUTERSLABELS`:
|
||||
Enable metrics on routers. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_ADDSERVICESLABELS`:
|
||||
Enable metrics on services. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_EXPLICITBOUNDARIES`:
|
||||
Boundaries for latency metrics. (Default: ```0.005000, 0.010000, 0.025000, 0.050000, 0.100000, 0.250000, 0.500000, 1.000000, 2.500000, 5.000000, 10.000000```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_GRPC`:
|
||||
gRPC specific configuration for the OpenTelemetry collector. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_HEADERS_<NAME>`:
|
||||
Headers sent with payload.
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_INSECURE`:
|
||||
Disables client transport security for the exporter. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_PATH`:
|
||||
Set the URL path of the collector endpoint.
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_PUSHINTERVAL`:
|
||||
Period between calls to collect a checkpoint. (Default: ```10```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_TLS_CA`:
|
||||
TLS CA
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_TLS_CERT`:
|
||||
TLS cert
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_TLS_INSECURESKIPVERIFY`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`TRAEFIK_METRICS_OPENTELEMETRY_TLS_KEY`:
|
||||
TLS key
|
||||
|
||||
`TRAEFIK_METRICS_PROMETHEUS`:
|
||||
Prometheus metrics exporter type. (Default: ```false```)
|
||||
|
||||
|
@ -1095,6 +1137,36 @@ Sets the sampling type. (Default: ```const```)
|
|||
`TRAEFIK_TRACING_JAEGER_TRACECONTEXTHEADERNAME`:
|
||||
Sets the header name used to store the trace ID. (Default: ```uber-trace-id```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY`:
|
||||
Settings for OpenTelemetry. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_ADDRESS`:
|
||||
Sets the address (host:port) of the collector endpoint. (Default: ```localhost:4318```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_GRPC`:
|
||||
gRPC specific configuration for the OpenTelemetry collector. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_HEADERS_<NAME>`:
|
||||
Defines additional headers to be sent with the payloads.
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_INSECURE`:
|
||||
Disables client transport security for the exporter. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_PATH`:
|
||||
Sets the URL path of the collector endpoint.
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_TLS_CA`:
|
||||
TLS CA
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_TLS_CERT`:
|
||||
TLS cert
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_TLS_INSECURESKIPVERIFY`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`TRAEFIK_TRACING_OPENTELEMETRY_TLS_KEY`:
|
||||
TLS key
|
||||
|
||||
`TRAEFIK_TRACING_SERVICENAME`:
|
||||
Set the name for this service. (Default: ```traefik```)
|
||||
|
||||
|
|
|
@ -307,6 +307,25 @@
|
|||
[metrics.influxDB2.additionalLabels]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[metrics.openTelemetry]
|
||||
address = "foobar"
|
||||
addEntryPointsLabels = true
|
||||
addRoutersLabels = true
|
||||
addServicesLabels = true
|
||||
pushInterval = "42s"
|
||||
path = "foobar"
|
||||
explicitBoundaries = [42.0, 42.0]
|
||||
insecure = true
|
||||
[metrics.openTelemetry.headers]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[metrics.openTelemetry.tls]
|
||||
ca = "foobar"
|
||||
caOptional = true
|
||||
cert = "foobar"
|
||||
insecureSkipVerify = true
|
||||
key = "foobar"
|
||||
[metrics.openTelemetry.grpc]
|
||||
|
||||
[ping]
|
||||
entryPoint = "foobar"
|
||||
|
@ -391,6 +410,20 @@
|
|||
serverURL = "foobar"
|
||||
secretToken = "foobar"
|
||||
serviceEnvironment = "foobar"
|
||||
[tracing.openTelemetry]
|
||||
address = "foobar"
|
||||
insecure = true
|
||||
path = "foobar"
|
||||
[tracing.openTelemetry.headers]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[tracing.openTelemetry.tls]
|
||||
ca = "foobar"
|
||||
caOptional = true
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
[tracing.openTelemetry.grpc]
|
||||
|
||||
[hostResolver]
|
||||
cnameFlattening = true
|
||||
|
@ -428,7 +461,6 @@
|
|||
|
||||
[experimental]
|
||||
kubernetesGateway = true
|
||||
http3 = true
|
||||
hub = true
|
||||
[experimental.plugins]
|
||||
[experimental.plugins.Descriptor0]
|
||||
|
|
|
@ -335,6 +335,28 @@ metrics:
|
|||
additionalLabels:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
openTelemetry:
|
||||
address: foobar
|
||||
addEntryPointsLabels: true
|
||||
addRoutersLabels: true
|
||||
addServicesLabels: true
|
||||
explicitBoundaries:
|
||||
- 42
|
||||
- 42
|
||||
headers:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
insecure: true
|
||||
path: foobar
|
||||
pushInterval: 42s
|
||||
tls:
|
||||
ca: foobar
|
||||
caOptional: true
|
||||
cert: foobar
|
||||
insecureSkipVerify: true
|
||||
key: foobar
|
||||
grpc: {}
|
||||
|
||||
ping:
|
||||
entryPoint: foobar
|
||||
manualRouting: true
|
||||
|
@ -417,6 +439,20 @@ tracing:
|
|||
serverURL: foobar
|
||||
secretToken: foobar
|
||||
serviceEnvironment: foobar
|
||||
openTelemetry:
|
||||
address: foobar
|
||||
headers:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
insecure: true
|
||||
path: foobar
|
||||
tls:
|
||||
ca: foobar
|
||||
caOptional: true
|
||||
cert: foobar
|
||||
key: foobar
|
||||
insecureSkipVerify: true
|
||||
grpc: {}
|
||||
hostResolver:
|
||||
cnameFlattening: true
|
||||
resolvConfig: foobar
|
||||
|
@ -453,7 +489,6 @@ hub:
|
|||
key: foobar
|
||||
experimental:
|
||||
kubernetesGateway: true
|
||||
http3: true
|
||||
hub: true
|
||||
plugins:
|
||||
Descriptor0:
|
||||
|
|
|
@ -312,39 +312,32 @@ entryPoints:
|
|||
#### `http3`
|
||||
|
||||
`http3` enables HTTP/3 protocol on the entryPoint.
|
||||
HTTP/3 requires a TCP entryPoint, as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP.
|
||||
In most scenarios, this entryPoint is the same as the one used for TLS traffic.
|
||||
HTTP/3 requires a TCP entryPoint,
|
||||
as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP.
|
||||
In most scenarios,
|
||||
this entryPoint is the same as the one used for TLS traffic.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
name:
|
||||
http3: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints.name.http3]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entrypoints.name.http3
|
||||
```
|
||||
|
||||
??? info "HTTP/3 uses UDP+TLS"
|
||||
|
||||
As HTTP/3 uses UDP, you can't have a TCP entryPoint with HTTP/3 on the same port as a UDP entryPoint.
|
||||
Since HTTP/3 requires the use of TLS, only routers with TLS enabled will be usable with HTTP/3.
|
||||
|
||||
!!! warning "Enabling Experimental HTTP/3"
|
||||
|
||||
As the HTTP/3 spec is still in draft, HTTP/3 support in Traefik is an experimental feature and needs to be activated
|
||||
in the experimental section of the static configuration.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
experimental:
|
||||
http3: true
|
||||
|
||||
entryPoints:
|
||||
name:
|
||||
http3: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[experimental]
|
||||
http3 = true
|
||||
|
||||
[entryPoints.name.http3]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--experimental.http3=true
|
||||
--entrypoints.name.http3
|
||||
```
|
||||
As HTTP/3 actually uses UDP, when traefik is configured with a TCP entryPoint on port N with HTTP/3 enabled,
|
||||
the underlying HTTP/3 server that is started automatically listens on UDP port N too. As a consequence,
|
||||
it means port N cannot be used by another UDP entryPoint.
|
||||
Since HTTP/3 requires the use of TLS,
|
||||
only routers with TLS enabled will be usable with HTTP/3.
|
||||
|
||||
#### `advertisedPort`
|
||||
|
||||
|
@ -355,9 +348,6 @@ It can be used to override the authority in the `alt-svc` header, for example if
|
|||
!!! info "http3.advertisedPort"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
experimental:
|
||||
http3: true
|
||||
|
||||
entryPoints:
|
||||
name:
|
||||
http3:
|
||||
|
@ -365,15 +355,11 @@ It can be used to override the authority in the `alt-svc` header, for example if
|
|||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[experimental]
|
||||
http3 = true
|
||||
|
||||
[entryPoints.name.http3]
|
||||
advertisedPort = 443
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--experimental.http3=true
|
||||
--entrypoints.name.http3.advertisedport=443
|
||||
```
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ The Kubernetes Ingress Controller, The Custom Resource Way.
|
|||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --log.level=DEBUG
|
||||
- --api
|
||||
|
|
|
@ -147,7 +147,7 @@ which in turn will create the resulting routers, services, handlers, etc.
|
|||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --entrypoints.web.address=:80
|
||||
- --providers.kubernetesingress
|
||||
|
@ -539,7 +539,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d
|
|||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --entrypoints.websecure.http.tls
|
||||
|
@ -749,7 +749,7 @@ For more options, please refer to the available [annotations](#on-ingress).
|
|||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --providers.kubernetesingress
|
||||
|
|
|
@ -214,78 +214,226 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
|||
Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria.
|
||||
If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service.
|
||||
|
||||
??? tip "Backticks or Quotes?"
|
||||
To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ``` ` ``` or escaped double-quotes `\"`.
|
||||
|
||||
Single quotes `'` are not accepted since the values are [Golang's String Literals](https://golang.org/ref/spec#String_literals).
|
||||
|
||||
!!! example "Host is example.com"
|
||||
|
||||
```toml
|
||||
rule = "Host(`example.com`)"
|
||||
```
|
||||
|
||||
!!! example "Host is example.com OR Host is example.org AND path is /traefik"
|
||||
|
||||
```toml
|
||||
rule = "Host(`example.com`) || (Host(`example.org`) && Path(`/traefik`))"
|
||||
```
|
||||
|
||||
The table below lists all the available matchers:
|
||||
|
||||
| Rule | Description |
|
||||
|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` |
|
||||
| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` |
|
||||
| ```Host(`example.com`, ...)``` | Check if the request domain (host header value) targets one of the given `domains`. |
|
||||
| ```HostHeader(`example.com`, ...)``` | Same as `Host`, only exists for historical reasons. |
|
||||
| ```HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Match the request domain. See "Regexp Syntax" below. |
|
||||
| ```Method(`GET`, ...)``` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`) |
|
||||
| ```Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)``` | Match exact request path. See "Regexp Syntax" below. |
|
||||
| ```PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)``` | Match request prefix path. See "Regexp Syntax" below. |
|
||||
| ```Query(`foo=bar`, `bar=baz`)``` | Match Query String parameters. It accepts a sequence of key=value pairs. |
|
||||
| ```ClientIP(`10.0.0.0/16`, `::1`)``` | Match if the request client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
| Rule | Description |
|
||||
|-----------------------------------------------------------------|:-------------------------------------------------------------------------------|
|
||||
| [```Header(`key`, `value`)```](#header-and-headerregexp) | Matches requests containing a header named `key` set to `value`. |
|
||||
| [```HeaderRegexp(`key`, `regexp`)```](#header-and-headerregexp) | Matches requests containing a header named `key` matching `regexp`. |
|
||||
| [```Host(`domain`)```](#host-and-hostregexp) | Matches requests host set to `domain`. |
|
||||
| [```HostRegexp(`regexp`)```](#host-and-hostregexp) | Matches requests host matching `regexp`. |
|
||||
| [```Method(`method`)```](#method) | Matches requests method set to `method`. |
|
||||
| [```Path(`path`)```](#path-pathprefix-and-pathregexp) | Matches requests path set to `path`. |
|
||||
| [```PathPrefix(`prefix`)```](#path-pathprefix-and-pathregexp) | Matches requests path prefix set to `prefix`. |
|
||||
| [```PathRegexp(`regexp`)```](#path-pathprefix-and-pathregexp) | Matches request path using `regexp`. |
|
||||
| [```Query(`key`, `value`)```](#query-and-queryregexp) | Matches requests query parameters named `key` set to `value`. |
|
||||
| [```QueryRegexp(`key`, `regexp`)```](#query-and-queryregexp) | Matches requests query parameters named `key` matching `regexp`. |
|
||||
| [```ClientIP(`ip`)```](#clientip) | Matches requests client IP using `ip`. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
|
||||
!!! important "Non-ASCII Domain Names"
|
||||
!!! tip "Backticks or Quotes?"
|
||||
|
||||
Non-ASCII characters are not supported in `Host` and `HostRegexp` expressions, and by doing so the associated router will be invalid.
|
||||
For the `Host` expression, domain names containing non-ASCII characters must be provided as punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)).
|
||||
As well, when using the `HostRegexp` expressions, in order to match domain names containing non-ASCII characters, the regular expression should match a punycode encoded domain name.
|
||||
To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ``` ` ``` or escaped double-quotes `\"`.
|
||||
|
||||
Single quotes `'` are not accepted since the values are [Go's String Literals](https://golang.org/ref/spec#String_literals).
|
||||
|
||||
!!! important "Regexp Syntax"
|
||||
|
||||
`HostRegexp`, `PathPrefix`, and `Path` accept an expression with zero or more groups enclosed by curly braces, which are called named regexps.
|
||||
Named regexps, of the form `{name:regexp}`, are the only expressions considered for regexp matching.
|
||||
The regexp name (`name` in the above example) is an arbitrary value, that exists only for historical reasons.
|
||||
Matchers that accept a regexp as their value use a [Go](https://golang.org/pkg/regexp/) flavored syntax.
|
||||
|
||||
Any `regexp` supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used.
|
||||
For example, here is a case insensitive path matcher syntax: ```Path(`/{path:(?i:Products)}`)```.
|
||||
|
||||
!!! info "Combining Matchers Using Operators and Parenthesis"
|
||||
!!! info "Expressing Complex Rules Using Operators and Parenthesis"
|
||||
|
||||
The usual AND (`&&`) and OR (`||`) logical operators can be used, with the expected precedence rules,
|
||||
as well as parentheses.
|
||||
|
||||
!!! info "Inverting a matcher"
|
||||
One can invert a matcher by using the NOT (`!`) operator.
|
||||
|
||||
One can invert a matcher by using the `!` operator.
|
||||
The following rule matches requests where:
|
||||
|
||||
!!! important "Rule, Middleware, and Services"
|
||||
- either host is `example.com` OR,
|
||||
- host is `example.org` AND path is NOT `/traefik`
|
||||
|
||||
The rule is evaluated "before" any middleware has the opportunity to work, and "before" the request is forwarded to the service.
|
||||
```yaml
|
||||
Host(`example.com`) || (Host(`example.org`) && !Path(`/traefik`))
|
||||
```
|
||||
|
||||
!!! info "Path Vs PathPrefix"
|
||||
#### Header and HeaderRegexp
|
||||
|
||||
Use `Path` if your service listens on the exact path only. For instance, ```Path(`/products`)``` would match `/products` but not `/products/shoes`.
|
||||
The `Header` and `HeaderRegexp` matchers allow to match requests that contain specific header.
|
||||
|
||||
Use a `*Prefix*` matcher if your service listens on a particular base path but also serves requests on sub-paths.
|
||||
For instance, ```PathPrefix(`/products`)``` would match `/products` and `/products/shoes`,
|
||||
as well as `/productsforsale`, and `/productsforsale/shoes`.
|
||||
Since the path is forwarded as-is, your service is expected to listen on `/products`.
|
||||
!!! example "Examples"
|
||||
|
||||
!!! info "ClientIP matcher"
|
||||
Match requests with a `Content-Type` header set to `application/yaml`:
|
||||
|
||||
The `ClientIP` matcher will only match the request client IP and does not use the `X-Forwarded-For` header for matching.
|
||||
```yaml
|
||||
Header(`Content-Type`, `application/yaml`)
|
||||
```
|
||||
|
||||
Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`:
|
||||
|
||||
```yaml
|
||||
HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`)
|
||||
```
|
||||
|
||||
To match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity), use the `(?i)` option:
|
||||
|
||||
```yaml
|
||||
HeaderRegexp(`Content-Type`, `(?i)^application/(json|yaml)$`)
|
||||
```
|
||||
|
||||
#### Host and HostRegexp
|
||||
|
||||
The `Host` and `HostRegexp` matchers allow to match requests that are targeted to a given host.
|
||||
|
||||
These matchers do not support non-ASCII characters, use punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)) to match such domains.
|
||||
|
||||
If no Host is set in the request URL (e.g., it's an IP address), these matchers will look at the `Host` header.
|
||||
|
||||
These matchers will match the request's host in lowercase.
|
||||
|
||||
!!! example "Examples"
|
||||
|
||||
Match requests with `Host` set to `example.com`:
|
||||
|
||||
```yaml
|
||||
Host(`example.com`)
|
||||
```
|
||||
|
||||
Match requests sent to any subdomain of `example.com`:
|
||||
|
||||
```yaml
|
||||
HostRegexp(`^.+\.example\.com$`)
|
||||
```
|
||||
|
||||
Match requests with `Host` set to either `example.com` or `example.org`:
|
||||
|
||||
```yaml
|
||||
HostRegexp(`^example\.(com|org)$`)
|
||||
```
|
||||
|
||||
To match domains [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity), use the `(?i)` option:
|
||||
|
||||
```yaml
|
||||
HostRegexp(`(?i)^example\.(com|org)$`)
|
||||
```
|
||||
|
||||
#### Method
|
||||
|
||||
The `Method` matchers allows to match requests sent with the given method.
|
||||
|
||||
!!! example "Example"
|
||||
|
||||
Match `OPTIONS` requests:
|
||||
|
||||
```yaml
|
||||
Method(`OPTIONS`)
|
||||
```
|
||||
|
||||
#### Path, PathPrefix, and PathRegexp
|
||||
|
||||
These matchers allow matching requests based on their URL path.
|
||||
|
||||
For exact matches, use `Path` and its prefixed alternative `PathPrefix`, for regexp matches, use `PathRegexp`.
|
||||
|
||||
Path are always starting with a `/`, except for `PathRegexp`.
|
||||
|
||||
!!! example "Examples"
|
||||
|
||||
Match `/products` but neither `/products/shoes` nor `/products/`:
|
||||
|
||||
```yaml
|
||||
Path(`/products`)
|
||||
```
|
||||
|
||||
Match `/products` as well as everything under `/products`,
|
||||
such as `/products/shoes`, `/products/` but also `/products-for-sale`:
|
||||
|
||||
```yaml
|
||||
PathPrefix(`/products`)
|
||||
```
|
||||
|
||||
Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/57`:
|
||||
|
||||
```yaml
|
||||
PathRegexp(`^/products/(shoes|socks)/[0-9]+$`)
|
||||
```
|
||||
|
||||
Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`:
|
||||
|
||||
```yaml
|
||||
PathRegexp(`\.(jpeg|jpg|png)$`)
|
||||
```
|
||||
|
||||
Match `/products` as well as everything under `/products`,
|
||||
such as `/products/shoes`, `/products/` but also `/products-for-sale`,
|
||||
[case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity):
|
||||
|
||||
```yaml
|
||||
HostRegexp(`(?i)^/products`)
|
||||
```
|
||||
|
||||
#### Query and QueryRegexp
|
||||
|
||||
The `Query` and `QueryRegexp` matchers allow to match requests based on query parameters.
|
||||
|
||||
!!! example "Examples"
|
||||
|
||||
Match requests with a `mobile` query parameter set to `true`, such as in `/search?mobile=true`:
|
||||
|
||||
```yaml
|
||||
Query(`mobile`, `true`)
|
||||
```
|
||||
|
||||
To match requests with a query parameter `mobile` that has no value, such as in `/search?mobile`, use:
|
||||
|
||||
```yaml
|
||||
Query(`mobile`)
|
||||
```
|
||||
|
||||
Match requests with a `mobile` query parameter set to either `true` or `yes`:
|
||||
|
||||
```yaml
|
||||
QueryRegexp(`mobile`, `^(true|yes)$`)
|
||||
```
|
||||
|
||||
Match requests with a `mobile` query parameter set to any value (including the empty value):
|
||||
|
||||
```yaml
|
||||
QueryRegexp(`mobile`, `^.*$`)
|
||||
```
|
||||
|
||||
To match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity), use the `(?i)` option:
|
||||
|
||||
```yaml
|
||||
QueryRegexp(`mobile`, `(?i)^(true|yes)$`)
|
||||
```
|
||||
|
||||
#### ClientIP
|
||||
|
||||
The `ClientIP` matcher allows matching requests sent from the given client IP.
|
||||
|
||||
It only matches the request client IP and does not use the `X-Forwarded-For` header for matching.
|
||||
|
||||
!!! example "Examples"
|
||||
|
||||
Match requests coming from a given IP:
|
||||
|
||||
```yaml tab="IPv4"
|
||||
ClientIP(`10.76.105.11`)
|
||||
```
|
||||
|
||||
```yaml tab="IPv6"
|
||||
ClientIP(`::1`)
|
||||
```
|
||||
|
||||
Match requests coming from a given subnet:
|
||||
|
||||
```yaml tab="IPv4"
|
||||
ClientIP(`192.168.1.0/24`)
|
||||
```
|
||||
|
||||
```yaml tab="IPv6"
|
||||
ClientIP(`fe80::/10`)
|
||||
```
|
||||
|
||||
### Priority
|
||||
|
||||
|
@ -300,7 +448,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
|||
http:
|
||||
routers:
|
||||
Router-1:
|
||||
rule: "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
|
||||
rule: "HostRegexp(`[a-z]+\.traefik\.com`)"
|
||||
# ...
|
||||
Router-2:
|
||||
rule: "Host(`foobar.traefik.com`)"
|
||||
|
@ -311,7 +459,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
|||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.Router-1]
|
||||
rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
|
||||
rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)"
|
||||
# ...
|
||||
[http.routers.Router-2]
|
||||
rule = "Host(`foobar.traefik.com`)"
|
||||
|
@ -320,10 +468,10 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
|||
|
||||
In this case, all requests with host `foobar.traefik.com` will be routed through `Router-1` instead of `Router-2`.
|
||||
|
||||
| Name | Rule | Priority |
|
||||
|----------|----------------------------------------------------|----------|
|
||||
| Router-1 | ```HostRegexp(`{subdomain:[a-z]+}.traefik.com`)``` | 44 |
|
||||
| Router-2 | ```Host(`foobar.traefik.com`)``` | 26 |
|
||||
| Name | Rule | Priority |
|
||||
|----------|------------------------------------------|----------|
|
||||
| Router-1 | ```HostRegexp(`[a-z]+\.traefik\.com`)``` | 44 |
|
||||
| Router-2 | ```Host(`foobar.traefik.com`)``` | 26 |
|
||||
|
||||
The previous table shows that `Router-1` has a higher priority than `Router-2`.
|
||||
|
||||
|
@ -336,7 +484,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
|||
http:
|
||||
routers:
|
||||
Router-1:
|
||||
rule: "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
|
||||
rule: "HostRegexp(`[a-z]+\\.traefik\\.com`)"
|
||||
entryPoints:
|
||||
- "web"
|
||||
service: service-1
|
||||
|
@ -353,7 +501,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
|||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.Router-1]
|
||||
rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
|
||||
rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)"
|
||||
entryPoints = ["web"]
|
||||
service = "service-1"
|
||||
priority = 1
|
||||
|
@ -685,9 +833,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
a situation where both sides are waiting for data and the
|
||||
connection appears to have hanged.
|
||||
|
||||
The only way that Traefik can deal with such a case, is to make
|
||||
sure that on the concerned entry point, there is no TLS router
|
||||
whatsoever (neither TCP nor HTTP), and there is at least one
|
||||
The only way that Traefik can deal with such a case, is to make
|
||||
sure that on the concerned entry point, there is no TLS router
|
||||
whatsoever (neither TCP nor HTTP), and there is at least one
|
||||
non-TLS TCP router that leads to the server in question.
|
||||
|
||||
??? example "Listens to Every Entry Point"
|
||||
|
@ -818,48 +966,49 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
|
||||
### Rule
|
||||
|
||||
Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria.
|
||||
Rules are a set of matchers configured with values, that determine if a particular connection matches specific criteria.
|
||||
If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service.
|
||||
|
||||
??? tip "Backticks or Quotes?"
|
||||
|
||||
To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ``` ` ``` or escaped double-quotes `\"`.
|
||||
|
||||
Single quotes `'` are not accepted since the values are [Golang's String Literals](https://golang.org/ref/spec#String_literals).
|
||||
|
||||
!!! example "HostSNI is example.com"
|
||||
|
||||
```toml
|
||||
rule = "HostSNI(`example.com`)"
|
||||
```
|
||||
|
||||
!!! example "HostSNI is example.com OR HostSNI is example.org AND ClientIP is 0.0.0.0"
|
||||
|
||||
```toml
|
||||
rule = "HostSNI(`example.com`) || (HostSNI(`example.org`) && ClientIP(`0.0.0.0`))"
|
||||
```
|
||||
|
||||
The table below lists all the available matchers:
|
||||
|
||||
| Rule | Description |
|
||||
|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| ```HostSNI(`domain-1`, ...)``` | Checks if the Server Name Indication corresponds to the given `domains`. |
|
||||
| ```HostSNIRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Checks if the Server Name Indication matches the given regular expressions. See "Regexp Syntax" below. |
|
||||
| ```ClientIP(`10.0.0.0/16`, `::1`)``` | Checks if the connection client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
| ```ALPN(`mqtt`, `h2c`)``` | Checks if any of the connection ALPN protocols is one of the given protocols. |
|
||||
| Rule | Description |
|
||||
|-------------------------------------------------------------|:-------------------------------------------------------------------------------------------------|
|
||||
| [```HostSNI(`domain`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication is equal to `domain`. |
|
||||
| [```HostSNIRegexp(`regexp`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication matches `regexp`. |
|
||||
| [```ClientIP(`ip`)```](#clientip_1) | Checks if the connection's client IP correspond to `ip`. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
| [```ALPN(`protocol`)```](#alpn) | Checks if the connection's ALPN protocol equals `protocol`. |
|
||||
|
||||
!!! important "Non-ASCII Domain Names"
|
||||
!!! tip "Backticks or Quotes?"
|
||||
|
||||
Non-ASCII characters are not supported in the `HostSNI` and `HostSNIRegexp` expressions, and so using them would invalidate the associated TCP router.
|
||||
Domain names containing non-ASCII characters must be provided as punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)).
|
||||
To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ``` ` ``` or escaped double-quotes `\"`.
|
||||
|
||||
Single quotes `'` are not accepted since the values are [Go's String Literals](https://golang.org/ref/spec#String_literals).
|
||||
|
||||
!!! important "Regexp Syntax"
|
||||
|
||||
`HostSNIRegexp` accepts an expression with zero or more groups enclosed by curly braces, which are called named regexps.
|
||||
Named regexps, of the form `{name:regexp}`, are the only expressions considered for regexp matching.
|
||||
The regexp name (`name` in the above example) is an arbitrary value, that exists only for historical reasons.
|
||||
Matchers that accept a regexp as their value use a [Go](https://golang.org/pkg/regexp/) flavored syntax.
|
||||
|
||||
Any `regexp` supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used.
|
||||
!!! info "Expressing Complex Rules Using Operators and Parenthesis"
|
||||
|
||||
The usual AND (`&&`) and OR (`||`) logical operators can be used, with the expected precedence rules,
|
||||
as well as parentheses.
|
||||
|
||||
One can invert a matcher by using the NOT (`!`) operator.
|
||||
|
||||
The following rule matches connections where:
|
||||
|
||||
- either Server Name Indication is `example.com` OR,
|
||||
- Server Name Indication is `example.org` AND ALPN protocol is NOT `h2`
|
||||
|
||||
```yaml
|
||||
HostSNI(`example.com`) || (HostSNI(`example.org`) && !ALPN(`h2`))
|
||||
```
|
||||
|
||||
#### HostSNI and HostSNIRegexp
|
||||
|
||||
`HostSNI` and `HostSNIRegexp` matchers allow to match connections targeted to a given domain.
|
||||
|
||||
These matchers do not support non-ASCII characters, use punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)) to match such domains.
|
||||
|
||||
!!! important "HostSNI & TLS"
|
||||
|
||||
|
@ -869,29 +1018,76 @@ The table below lists all the available matchers:
|
|||
when one wants a non-TLS router that matches all (non-TLS) requests,
|
||||
one should use the specific ```HostSNI(`*`)``` syntax.
|
||||
|
||||
!!! info "Combining Matchers Using Operators and Parenthesis"
|
||||
!!! example "Examples"
|
||||
|
||||
The usual AND (`&&`) and OR (`||`) logical operators can be used, with the expected precedence rules,
|
||||
as well as parentheses.
|
||||
Match all connections:
|
||||
|
||||
!!! info "Inverting a matcher"
|
||||
```yaml tab="HostSNI"
|
||||
HostSNI(`*`)
|
||||
```
|
||||
|
||||
One can invert a matcher by using the `!` operator.
|
||||
```yaml tab="HostSNIRegexp"
|
||||
HostSNIRegexp(`^.*$`)
|
||||
```
|
||||
|
||||
!!! important "Rule, Middleware, and Services"
|
||||
Match TCP connections sent to `example.com`:
|
||||
|
||||
The rule is evaluated "before" any middleware has the opportunity to work, and "before" the request is forwarded to the service.
|
||||
```yaml
|
||||
HostSNI(`example.com`)
|
||||
```
|
||||
|
||||
!!! important "ALPN ACME-TLS/1"
|
||||
Match TCP connections openned on any subdomain of `example.com`:
|
||||
|
||||
It would be a security issue to let a user-defined router catch the response to
|
||||
an ACME TLS challenge previously initiated by Traefik.
|
||||
For this reason, the `ALPN` matcher is not allowed to match the `ACME-TLS/1`
|
||||
protocol, and Traefik returns an error if this is attempted.
|
||||
```yaml
|
||||
HostSNIRegexp(`^.+\.example\.com$`)
|
||||
```
|
||||
|
||||
#### ClientIP
|
||||
|
||||
The `ClientIP` matcher allows matching connections opened by a client with the given IP.
|
||||
|
||||
!!! example "Examples"
|
||||
|
||||
Match connections opened by a given IP:
|
||||
|
||||
```yaml tab="IPv4"
|
||||
ClientIP(`10.76.105.11`)
|
||||
```
|
||||
|
||||
```yaml tab="IPv6"
|
||||
ClientIP(`::1`)
|
||||
```
|
||||
|
||||
Match connections coming from a given subnet:
|
||||
|
||||
```yaml tab="IPv4"
|
||||
ClientIP(`192.168.1.0/24`)
|
||||
```
|
||||
|
||||
```yaml tab="IPv6"
|
||||
ClientIP(`fe80::/10`)
|
||||
```
|
||||
|
||||
#### ALPN
|
||||
|
||||
The `ALPN` matcher allows matching connections the given protocol.
|
||||
|
||||
It would be a security issue to let a user-defined router catch the response to
|
||||
an ACME TLS challenge previously initiated by Traefik.
|
||||
For this reason, the `ALPN` matcher is not allowed to match the `ACME-TLS/1`
|
||||
protocol, and Traefik returns an error if this is attempted.
|
||||
|
||||
!!! example "Example"
|
||||
|
||||
Match connections using the ALPN protocol `h2`:
|
||||
|
||||
```yaml
|
||||
ALPN(`h2`)
|
||||
```
|
||||
|
||||
### Priority
|
||||
|
||||
To avoid path overlap, routes are sorted, by default, in descending order using rules length.
|
||||
To avoid path overlap, routes are sorted, by default, in descending order using rules length.
|
||||
The priority is directly equal to the length of the rule, and so the longest length has the highest priority.
|
||||
|
||||
A value of `0` for the priority is ignored: `priority = 0` means that the default rules length sorting is used.
|
||||
|
@ -1221,8 +1417,8 @@ So UDP "routers" at this time are pretty much only load-balancers in one form or
|
|||
It basically means that some state is kept about an ongoing communication between a client and a backend,
|
||||
notably so that the proxy knows where to forward a response packet from a backend.
|
||||
As expected, a `timeout` is associated to each of these sessions,
|
||||
so that they get cleaned out if they go through a period of inactivity longer than a given duration.
|
||||
Timeout can be configured using the `entryPoints.name.udp.timeout` option as described
|
||||
so that they get cleaned out if they go through a period of inactivity longer than a given duration.
|
||||
Timeout can be configured using the `entryPoints.name.udp.timeout` option as described
|
||||
under [EntryPoints](../entrypoints/#udp-options).
|
||||
|
||||
### EntryPoints
|
||||
|
|
|
@ -29,6 +29,12 @@ This agent can:
|
|||
* The Traefik Hub Agent must be installed to connect to the Traefik Hub platform.
|
||||
* Activate this feature in the experimental section of the static configuration.
|
||||
|
||||
!!! information "Configuration Discovery"
|
||||
|
||||
According to installation options, the Traefik Hub Agent listens to the Docker or Kubernetes API to discover containers/services.
|
||||
|
||||
It doesn't support the routers discovered by Traefik Proxy using other providers, e.g., using the File provider.
|
||||
|
||||
!!! example "Minimal Static Configuration to Activate Traefik Hub for Docker"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
|
|
|
@ -26,7 +26,7 @@ spec:
|
|||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.9
|
||||
image: traefik:v3.0
|
||||
args:
|
||||
- --api.insecure
|
||||
- --accesslog
|
||||
|
|
|
@ -26,5 +26,5 @@ node:
|
|||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||
volumes:
|
||||
# this is where you would place a alternative traefik image (saved as a .tar file with
|
||||
# 'docker save'), if you want to use it, instead of the traefik:v2.9 image.
|
||||
# 'docker save'), if you want to use it, instead of the traefik:v3.0 image.
|
||||
- /somewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images
|
||||
|
|
|
@ -3,7 +3,7 @@ version: "3.3"
|
|||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
image: "traefik:v3.0"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
|
|
@ -13,7 +13,7 @@ secrets:
|
|||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
image: "traefik:v3.0"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
|
|
@ -3,7 +3,7 @@ version: "3.3"
|
|||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
image: "traefik:v3.0"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
|
|
@ -3,7 +3,7 @@ version: "3.3"
|
|||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
image: "traefik:v3.0"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
|
|
@ -3,7 +3,7 @@ version: "3.3"
|
|||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
image: "traefik:v3.0"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
|
|
@ -16,6 +16,35 @@ This will also be used as a starting point for the other docker-compose guides.
|
|||
--8<-- "content/user-guides/docker-compose/basic-example/docker-compose.yml"
|
||||
```
|
||||
|
||||
??? Networking
|
||||
|
||||
The Traefik container has to be attached to the same network as the containers to be exposed.
|
||||
If no networks are specified in the docker-compose file, Docker creates a default one that allows Traefik to reach the containers defined in the same file.
|
||||
You can [customize the network](https://docs.docker.com/compose/networking/#specify-custom-networks) as described in the example below.
|
||||
You can use a [pre-existing network](https://docs.docker.com/compose/networking/#use-a-pre-existing-network) too.
|
||||
|
||||
```yaml
|
||||
version: "3.3"
|
||||
|
||||
networks:
|
||||
traefiknet: {}
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v3.0"
|
||||
...
|
||||
networks:
|
||||
- traefiknet
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
...
|
||||
networks:
|
||||
- traefiknet
|
||||
|
||||
```
|
||||
|
||||
- Replace `whoami.localhost` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `http://your_own_domain` to confirm everything went fine.
|
||||
|
|
|
@ -157,6 +157,7 @@ nav:
|
|||
- 'Datadog': 'observability/metrics/datadog.md'
|
||||
- 'InfluxDB': 'observability/metrics/influxdb.md'
|
||||
- 'InfluxDB2': 'observability/metrics/influxdb2.md'
|
||||
- 'OpenTelemetry': 'observability/metrics/opentelemetry.md'
|
||||
- 'Prometheus': 'observability/metrics/prometheus.md'
|
||||
- 'StatsD': 'observability/metrics/statsd.md'
|
||||
- 'Tracing':
|
||||
|
@ -167,6 +168,7 @@ nav:
|
|||
- 'Instana': 'observability/tracing/instana.md'
|
||||
- 'Haystack': 'observability/tracing/haystack.md'
|
||||
- 'Elastic': 'observability/tracing/elastic.md'
|
||||
- 'OpenTelemetry': 'observability/tracing/opentelemetry.md'
|
||||
- 'User Guides':
|
||||
- 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md'
|
||||
- 'gRPC Examples': 'user-guides/grpc.md'
|
||||
|
|
105
go.mod
105
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/docker/go-connections v0.4.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
|
||||
github.com/go-acme/lego/v4 v4.9.0
|
||||
github.com/go-acme/lego/v4 v4.9.1
|
||||
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.5.2
|
||||
|
@ -47,7 +47,7 @@ require (
|
|||
github.com/lucas-clemente/quic-go v0.28.1
|
||||
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/natefinch/lumberjack v0.0.0-20201021141957-47ffae23317c
|
||||
|
@ -77,33 +77,45 @@ require (
|
|||
github.com/vulcand/predicate v1.2.0
|
||||
go.elastic.co/apm v1.13.1
|
||||
go.elastic.co/apm/module/apmot v1.13.1
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/text v0.4.0
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
|
||||
golang.org/x/tools v0.1.12
|
||||
google.golang.org/grpc v1.46.0
|
||||
go.opentelemetry.io/collector/pdata v0.64.1
|
||||
go.opentelemetry.io/otel v1.11.1
|
||||
go.opentelemetry.io/otel/bridge/opentracing v1.11.1
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.33.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.33.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1
|
||||
go.opentelemetry.io/otel/metric v0.33.0
|
||||
go.opentelemetry.io/otel/sdk v1.11.1
|
||||
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
||||
go.opentelemetry.io/otel/trace v1.11.1
|
||||
golang.org/x/exp v0.0.0-20221114191408-850992195362
|
||||
golang.org/x/mod v0.6.0
|
||||
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10
|
||||
golang.org/x/text v0.5.0
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
||||
golang.org/x/tools v0.2.0
|
||||
google.golang.org/grpc v1.50.1
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.43.1
|
||||
gopkg.in/fsnotify.v1 v1.4.7
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.22.1
|
||||
k8s.io/apiextensions-apiserver v0.21.3
|
||||
k8s.io/apimachinery v0.22.1
|
||||
k8s.io/client-go v0.22.1
|
||||
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e
|
||||
k8s.io/api v0.25.0
|
||||
k8s.io/apiextensions-apiserver v0.25.0
|
||||
k8s.io/apimachinery v0.25.0
|
||||
k8s.io/client-go v0.25.0
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
mvdan.cc/xurls/v2 v2.1.0
|
||||
sigs.k8s.io/gateway-api v0.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
cloud.google.com/go v0.97.0 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v40.3.0+incompatible // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
|
@ -121,6 +133,8 @@ require (
|
|||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.23 // indirect
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/Shopify/sarama v1.23.1 // indirect
|
||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
||||
|
@ -165,7 +179,8 @@ require (
|
|||
github.com/elastic/go-licenser v0.3.1 // indirect
|
||||
github.com/elastic/go-sysinfo v1.1.1 // indirect
|
||||
github.com/elastic/go-windows v1.0.0 // indirect
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/exoscale/egoscale v0.90.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
|
@ -173,7 +188,11 @@ require (
|
|||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
|
@ -182,21 +201,22 @@ require (
|
|||
github.com/gogo/googleapis v1.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/consul/sdk v0.10.0 // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.1 // indirect
|
||||
|
@ -237,6 +257,7 @@ require (
|
|||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||
github.com/looplab/fsm v0.1.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
|
||||
github.com/mailgun/multibuf v0.1.2 // indirect
|
||||
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect
|
||||
|
@ -256,7 +277,7 @@ require (
|
|||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/mount v0.2.0 // indirect
|
||||
|
@ -265,6 +286,7 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/desec v0.6.0 // indirect
|
||||
|
@ -300,7 +322,7 @@ require (
|
|||
github.com/softlayer/softlayer-go v1.0.6 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1 // indirect
|
||||
github.com/spf13/cobra v1.4.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
||||
|
@ -325,22 +347,26 @@ require (
|
|||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.4 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/ratelimit v0.2.0 // indirect
|
||||
go.uber.org/zap v1.18.1 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/term v0.1.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/api v0.44.0 // indirect
|
||||
google.golang.org/api v0.57.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.6 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.5 // indirect
|
||||
|
@ -349,11 +375,12 @@ require (
|
|||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
|
||||
inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect
|
||||
k8s.io/klog/v2 v2.10.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
|
||||
k8s.io/klog/v2 v2.70.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
// Containous forks
|
||||
|
@ -370,3 +397,5 @@ replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402
|
|||
// ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules
|
||||
// tencentcloud uses monorepo with multimodule but the go.mod files are incomplete.
|
||||
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
||||
|
||||
// replace github.com/go-logr/logr => github.com/go-logr/logr v0.4.0
|
||||
|
|
232
go.sum
232
go.sum
|
@ -24,8 +24,15 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
|
|||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
|
@ -70,8 +77,9 @@ github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUd
|
|||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE=
|
||||
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
||||
github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
|
||||
github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
|
@ -79,8 +87,9 @@ github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMl
|
|||
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
|
||||
|
@ -95,8 +104,9 @@ github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxB
|
|||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
|
@ -175,8 +185,10 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
|||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
|
@ -480,6 +492,7 @@ github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobe
|
|||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
|
@ -608,6 +621,8 @@ github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/El
|
|||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
|
||||
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -624,8 +639,9 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a
|
|||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exoscale/egoscale v0.90.0 h1:DZBXVU3iHqu5Ju5lQ5jWVlPo0IpI98SUo8Aa1UQVrmo=
|
||||
github.com/exoscale/egoscale v0.90.0/go.mod h1:wyXE5zrnFynMXA0jMhwQqSe24CfUhmBk2WI5wFZcq6Y=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
|
@ -669,8 +685,8 @@ github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX
|
|||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
|
||||
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-acme/lego/v4 v4.9.1 h1:n9Z5MQwANeGSQKlVE3bEh9SDvAySK9oVYOKCGCESqQE=
|
||||
github.com/go-acme/lego/v4 v4.9.1/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
|
@ -695,17 +711,24 @@ github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNV
|
|||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
|
@ -713,6 +736,7 @@ github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHK
|
|||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
|
@ -784,8 +808,9 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
|
|||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -826,6 +851,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
|||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
|
@ -836,6 +862,8 @@ github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
|||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -849,8 +877,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-containerregistry v0.0.0-20191015185424-71da34e4d9b3/go.mod h1:ZXFeSndFcK4vB1NR4voH1Zm38K7ViUNiYtfIBDxrwf0=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
|
@ -869,6 +897,7 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
|
@ -882,6 +911,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
|
@ -895,15 +926,15 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
|
@ -929,13 +960,16 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
|
|||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
|
@ -1249,8 +1283,9 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
|
|||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mailgun/multibuf v0.1.2 h1:QE9kE27lK6LFZB4aYNVtUPlWVHVCT0zpgUr2uoq/+jk=
|
||||
github.com/mailgun/multibuf v0.1.2/go.mod h1:E+sUhIy69qgT6EM57kCPdUTlHnjTuxQBO/yf6af9Hes=
|
||||
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 h1:Kg/NPZLLC3aAFr1YToMs98dbCdhootQ1hZIvZU28hAQ=
|
||||
|
@ -1326,8 +1361,9 @@ github.com/mimuret/golang-iij-dpf v0.7.1/go.mod h1:IXWYcQVIHYzuM+W7kDWX0mseHDfUo
|
|||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
|
@ -1358,8 +1394,9 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQ
|
|||
github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
|
||||
github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf h1:dHwWBX8OhYb69qVcT27rFSwzKsn4CRbg0HInQyVh33c=
|
||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf/go.mod h1:GJcrUlTGFAPlEmPQtbrTsJYn+cy+Jwl7vTZS7jYAoow=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
|
@ -1393,6 +1430,7 @@ github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1
|
|||
github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
|
@ -1456,6 +1494,7 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
|
@ -1751,8 +1790,9 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL
|
|||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
|
@ -1956,26 +1996,60 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/collector/pdata v0.64.1 h1:8E06uHr0nnenGftFwhwdenA88QhVnF4dJam+qVXgdVg=
|
||||
go.opentelemetry.io/collector/pdata v0.64.1/go.mod h1:IzvXUGQml2mrnvdb8zIlEW3qQs9oFLdD2hLwJdZ+pek=
|
||||
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
||||
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
||||
go.opentelemetry.io/otel/bridge/opentracing v1.11.1 h1:/ZBsgjXWUpiZ5M9zm+Ft3kuDUGErIGcEJbKRIsFN6jA=
|
||||
go.opentelemetry.io/otel/bridge/opentracing v1.11.1/go.mod h1:vw9hN4H+G0ek+XQtxP+Mm1McLcmdx2FXHNrWn2bBqxU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.33.0 h1:OT/UjHcjog4A1s1UMCtyehIKS+vpjM5Du0r7KGsH6TE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.33.0/go.mod h1:0XctNDHEWmiSDIU8NPbJElrK05gBJFcYlGP4FMGo4g4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.33.0 h1:1SVtGtRsNyGgv1fRfNXfh+sJowIwzF0gkf+61lvTgdg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.33.0/go.mod h1:ryB27ubOBXsiqfh6MwtSdx5knzbSZtjvPnMMmt3AykQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.33.0 h1:NoG4v01cdLZfOeNGBQmSe4f4SeP+fx8I/0qzRgTKsGI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.33.0/go.mod h1:6anbDXBcTp3Qit87pfFmT0paxTJ8sWRccTNYVywN/H8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 h1:MEQNafcNCB0uQIti/oHgU7CZpUMYQ7qigBwMVKycHvc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1/go.mod h1:19O5I2U5iys38SsmT2uDJja/300woyzE1KPIQxEUBUc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 h1:LYyG/f1W/jzAix16jbksJfMQFpOH/Ma6T639pVPMgfI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1/go.mod h1:QrRRQiY3kzAoYPNLP0W/Ikg0gR6V3LMc+ODSxr7yyvg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1 h1:tFl63cpAAcD9TOU6U8kZU7KyXuSRYAZlbx1C61aaB74=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.1/go.mod h1:X620Jww3RajCJXw/unA+8IRTgxkdS7pi+ZwK9b7KUJk=
|
||||
go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E=
|
||||
go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI=
|
||||
go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs=
|
||||
go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys=
|
||||
go.opentelemetry.io/otel/sdk/metric v0.33.0 h1:oTqyWfksgKoJmbrs2q7O7ahkJzt+Ipekihf8vhpa9qo=
|
||||
go.opentelemetry.io/otel/sdk/metric v0.33.0/go.mod h1:xdypMeA21JBOvjjzDUtD0kzIcHO/SPez+a8HOzJPGp0=
|
||||
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
||||
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
|
||||
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||
|
@ -2028,8 +2102,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -2044,7 +2118,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925 h1:5XVKs2rlCg8EFyRcvO8/XFwYxh1oKJO1Q3X5vttIf9c=
|
||||
golang.org/x/exp v0.0.0-20221114191408-850992195362 h1:NoHlPRbyl1VFI6FjwHtPQCN7wAMXI6cKcqrmXhOOfBQ=
|
||||
golang.org/x/exp v0.0.0-20221114191408-850992195362/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -2074,8 +2149,8 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM
|
|||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -2138,6 +2213,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -2148,8 +2224,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc=
|
||||
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -2166,6 +2242,10 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA=
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
|
@ -2296,12 +2376,16 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -2311,14 +2395,14 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -2329,8 +2413,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -2340,8 +2424,8 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -2415,10 +2499,12 @@ golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -2457,8 +2543,15 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
|
|||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -2525,9 +2618,25 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c h1:FqrtZMB5Wr+/RecOM3uPJNPfWR8Upb5hAPnt7PU6i4k=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
|
@ -2558,11 +2667,18 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
|
|||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/grpc/examples v0.0.0-20201130180447-c456688b1860/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
@ -2577,8 +2693,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.43.1 h1:Dez4VzRQWAI5YXJRBx58BiC0gONGuW/oY4l8fWKzOXY=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.43.1/go.mod h1:YL9g+nlUY7ByCffD5pDytAqy99GNbytRV0EBpKuldM4=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
|
@ -2676,10 +2793,12 @@ k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
|
|||
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
|
||||
k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU=
|
||||
k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg=
|
||||
k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY=
|
||||
k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY=
|
||||
k8s.io/apiextensions-apiserver v0.21.3 h1:+B6biyUWpqt41kz5x6peIsljlsuwvNAp/oFax/j2/aY=
|
||||
k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0=
|
||||
k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk=
|
||||
k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE=
|
||||
k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY=
|
||||
k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E=
|
||||
k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/apimachinery v0.0.0-20190806215851-162a2dabc72f/go.mod h1:+ntn62igV2hyNj7/0brOvXSMONE2KxcePkSxK7/9FFQ=
|
||||
k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
|
||||
|
@ -2689,8 +2808,9 @@ k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp
|
|||
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
|
||||
k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
|
||||
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
|
||||
k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM=
|
||||
k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
|
||||
k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU=
|
||||
k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
|
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||
k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
|
||||
k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
|
||||
|
@ -2703,8 +2823,9 @@ k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
|
|||
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
|
||||
k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA=
|
||||
k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU=
|
||||
k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw=
|
||||
k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
|
||||
k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E=
|
||||
k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8=
|
||||
k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
|
||||
k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
|
||||
|
@ -2730,23 +2851,26 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
|||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE=
|
||||
k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
|
||||
k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
|
||||
k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e h1:ldQh+neBabomh7+89dTpiFAB8tGdfVmuIzAHbvtl+9I=
|
||||
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
|
||||
mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
|
||||
|
@ -2764,15 +2888,19 @@ sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCb
|
|||
sigs.k8s.io/controller-tools v0.6.2/go.mod h1:oaeGpjXn6+ZSEIQkUe/+3I40PNiDYp9aeawbt3xTgJ8=
|
||||
sigs.k8s.io/gateway-api v0.4.0 h1:07IJkTt21NetZTHtPKJk2I4XIgDN4BAlTIq1wK7V11o=
|
||||
sigs.k8s.io/gateway-api v0.4.0/go.mod h1:r3eiNP+0el+NTLwaTfOrCNXy8TukC+dIM3ggc+fbNWk=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
|
60
integration/fixtures/https/https_invalid_tls_options.toml
Normal file
60
integration/fixtures/https/https_invalid_tls_options.toml
Normal file
|
@ -0,0 +1,60 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints.websecure]
|
||||
address = ":4443"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.router1]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Host(`snitest.com`)"
|
||||
[http.routers.router1.tls]
|
||||
options = "invalidTLSOptions"
|
||||
|
||||
[http.routers.router2]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Host(`snitest.org`)"
|
||||
[http.routers.router2.tls]
|
||||
|
||||
# fallback router
|
||||
[http.routers.router3]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Path(`/`)"
|
||||
[http.routers.router3.tls]
|
||||
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:9010"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default.clientAuth]
|
||||
# Missing caFile to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
|
||||
[tls.options.invalidTLSOptions.clientAuth]
|
||||
# Missing caFile to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
|
@ -20,12 +20,12 @@
|
|||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
rule = "HostRegexp(`{subdomain:[a-z1-9-]+}.snitest.com`)"
|
||||
rule = "HostRegexp(`[a-z1-9-]+\\.snitest\\.com`)"
|
||||
service = "service1"
|
||||
[http.routers.router1.tls]
|
||||
|
||||
[http.routers.router2]
|
||||
rule = "HostRegexp(`{subdomain:[a-z1-9-]+}.www.snitest.com`)"
|
||||
rule = "HostRegexp(`[a-z1-9-]+\\.www\\.snitest\\.com`)"
|
||||
service = "service1"
|
||||
[http.routers.router2.tls]
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -56,11 +56,11 @@ spec:
|
|||
- Rule
|
||||
type: string
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule'
|
||||
type: string
|
||||
middlewares:
|
||||
description: 'Middlewares defines the list of references to
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
Middleware resources. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware'
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
|
@ -79,7 +79,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of Service. It can contain
|
||||
|
@ -145,7 +145,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -190,16 +190,16 @@ spec:
|
|||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls'
|
||||
description: 'TLS defines the TLS configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -217,15 +217,15 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSOption.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
TLSOption. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -241,11 +241,11 @@ spec:
|
|||
properties:
|
||||
name:
|
||||
description: 'Name defines the name of the referenced TLSStore.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace defines the namespace of the referenced
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
TLSStore. More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore'
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -307,7 +307,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -318,7 +318,7 @@ spec:
|
|||
description: RouteTCP holds the TCP route configuration.
|
||||
properties:
|
||||
match:
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule_1'
|
||||
description: 'Match defines the router''s rule. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1'
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to MiddlewareTCP
|
||||
|
@ -341,7 +341,7 @@ spec:
|
|||
type: array
|
||||
priority:
|
||||
description: 'Priority defines the router''s priority. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority_1'
|
||||
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1'
|
||||
type: integer
|
||||
services:
|
||||
description: Services defines the list of TCP services.
|
||||
|
@ -366,7 +366,7 @@ spec:
|
|||
x-kubernetes-int-or-string: true
|
||||
proxyProtocol:
|
||||
description: 'ProxyProtocol defines the PROXY protocol
|
||||
configuration. More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol'
|
||||
configuration. More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol'
|
||||
properties:
|
||||
version:
|
||||
description: Version defines the PROXY Protocol version
|
||||
|
@ -397,16 +397,16 @@ spec:
|
|||
type: array
|
||||
tls:
|
||||
description: 'TLS defines the TLS configuration on a layer 4 / TCP
|
||||
Route. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls_1'
|
||||
Route. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1'
|
||||
properties:
|
||||
certResolver:
|
||||
description: 'CertResolver defines the name of the certificate
|
||||
resolver to use. Cert resolvers have to be configured in the
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers'
|
||||
static configuration. More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers'
|
||||
type: string
|
||||
domains:
|
||||
description: 'Domains defines the list of domains that will be
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains'
|
||||
used to issue certificates. More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains'
|
||||
items:
|
||||
description: Domain holds a domain name with SANs.
|
||||
properties:
|
||||
|
@ -424,7 +424,7 @@ spec:
|
|||
options:
|
||||
description: 'Options defines the reference to a TLSOption, that
|
||||
specifies the parameters of the TLS connection. If not defined,
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
the `default` TLSOption is used. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Traefik
|
||||
|
@ -518,7 +518,7 @@ spec:
|
|||
entryPoints:
|
||||
description: 'EntryPoints defines the list of entry point names to
|
||||
bind to. Entry points have to be configured in the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
Default: all.'
|
||||
items:
|
||||
type: string
|
||||
|
@ -597,7 +597,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'Middleware is the CRD implementation of a Traefik Middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -617,7 +617,7 @@ spec:
|
|||
addPrefix:
|
||||
description: 'AddPrefix holds the add prefix middleware configuration.
|
||||
This middleware updates the path of a request before forwarding
|
||||
it. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/addprefix/'
|
||||
it. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/'
|
||||
properties:
|
||||
prefix:
|
||||
description: Prefix is the string to add before the current path
|
||||
|
@ -627,11 +627,11 @@ spec:
|
|||
basicAuth:
|
||||
description: 'BasicAuth holds the basic auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -651,7 +651,7 @@ spec:
|
|||
buffering:
|
||||
description: 'Buffering holds the buffering middleware configuration.
|
||||
This middleware retries or limits the size of requests that can
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
be forwarded to backends. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes'
|
||||
properties:
|
||||
maxRequestBodyBytes:
|
||||
description: 'MaxRequestBodyBytes defines the maximum allowed
|
||||
|
@ -684,13 +684,13 @@ spec:
|
|||
retryExpression:
|
||||
description: 'RetryExpression defines the retry conditions. It
|
||||
is a logical combination of functions with operators AND (&&)
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#retryexpression'
|
||||
and OR (||). More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression'
|
||||
type: string
|
||||
type: object
|
||||
chain:
|
||||
description: 'Chain holds the configuration of the chain middleware.
|
||||
This middleware enables to define reusable combinations of other
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/chain/'
|
||||
pieces of middleware. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/'
|
||||
properties:
|
||||
middlewares:
|
||||
description: Middlewares is the list of MiddlewareRef which composes
|
||||
|
@ -744,7 +744,7 @@ spec:
|
|||
compress:
|
||||
description: 'Compress holds the compress middleware configuration.
|
||||
This middleware compresses responses before sending them to the
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/compress/'
|
||||
client, using gzip compression. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/'
|
||||
properties:
|
||||
excludedContentTypes:
|
||||
description: ExcludedContentTypes defines the list of content
|
||||
|
@ -762,28 +762,18 @@ spec:
|
|||
type: object
|
||||
contentType:
|
||||
description: ContentType holds the content-type middleware configuration.
|
||||
This middleware exists to enable the correct behavior until at least
|
||||
the default one can be changed in a future version.
|
||||
properties:
|
||||
autoDetect:
|
||||
description: AutoDetect specifies whether to let the `Content-Type`
|
||||
header, if it has not been set by the backend, be automatically
|
||||
set to a value derived from the contents of the response. As
|
||||
a proxy, the default behavior should be to leave the header
|
||||
alone, regardless of what the backend did with it. However,
|
||||
the historic default was to always auto-detect and set the header
|
||||
if it was nil, and it is going to be kept that way in order
|
||||
to support users currently relying on it.
|
||||
type: boolean
|
||||
This middleware sets the `Content-Type` header value to the media
|
||||
type detected from the response content, when it is not set by the
|
||||
backend.
|
||||
type: object
|
||||
digestAuth:
|
||||
description: 'DigestAuth holds the digest auth middleware configuration.
|
||||
This middleware restricts access to your services to known users.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/digestauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/'
|
||||
properties:
|
||||
headerField:
|
||||
description: 'HeaderField defines a header field to store the
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield'
|
||||
authenticated user. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield'
|
||||
type: string
|
||||
realm:
|
||||
description: 'Realm allows the protected resources on a server
|
||||
|
@ -802,7 +792,7 @@ spec:
|
|||
errors:
|
||||
description: 'ErrorPage holds the custom error middleware configuration.
|
||||
This middleware returns a custom page in lieu of the default, according
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/'
|
||||
to configured ranges of HTTP Status codes. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/'
|
||||
properties:
|
||||
query:
|
||||
description: Query defines the URL for the error page (hosted
|
||||
|
@ -811,7 +801,7 @@ spec:
|
|||
type: string
|
||||
service:
|
||||
description: 'Service defines the reference to a Kubernetes Service
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/#service'
|
||||
that will serve the error page. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service'
|
||||
properties:
|
||||
kind:
|
||||
description: Kind defines the kind of the Service.
|
||||
|
@ -868,7 +858,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -917,7 +907,7 @@ spec:
|
|||
forwardAuth:
|
||||
description: 'ForwardAuth holds the forward auth middleware configuration.
|
||||
This middleware delegates the request authentication to a Service.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
|
||||
properties:
|
||||
address:
|
||||
description: Address defines the authentication server address.
|
||||
|
@ -940,7 +930,7 @@ spec:
|
|||
description: 'AuthResponseHeadersRegex defines the regex to match
|
||||
headers to copy from the authentication server response and
|
||||
set on forwarded request, after stripping all headers that match
|
||||
the regex. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
the regex. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex'
|
||||
type: string
|
||||
tls:
|
||||
description: TLS defines the configuration used to secure the
|
||||
|
@ -980,7 +970,7 @@ spec:
|
|||
headers:
|
||||
description: 'Headers holds the headers middleware configuration.
|
||||
This middleware manages the requests and responses headers. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/headers/#customrequestheaders'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders'
|
||||
properties:
|
||||
accessControlAllowCredentials:
|
||||
description: AccessControlAllowCredentials defines whether the
|
||||
|
@ -1124,7 +1114,7 @@ spec:
|
|||
inFlightReq:
|
||||
description: 'InFlightReq holds the in-flight request middleware configuration.
|
||||
This middleware limits the number of requests being processed and
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/'
|
||||
served concurrently. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/'
|
||||
properties:
|
||||
amount:
|
||||
description: Amount defines the maximum amount of allowed simultaneous
|
||||
|
@ -1138,11 +1128,11 @@ spec:
|
|||
group requests as originating from a common source. If several
|
||||
strategies are defined at the same time, an error will be raised.
|
||||
If none are set, the default is to use the requestHost. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/#sourcecriterion'
|
||||
info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1170,11 +1160,11 @@ spec:
|
|||
ipAllowList:
|
||||
description: 'IPAllowList holds the IP allowlist middleware configuration.
|
||||
This middleware accepts / refuses requests based on the client IP.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/'
|
||||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration used
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1198,7 +1188,7 @@ spec:
|
|||
passTLSClientCert:
|
||||
description: 'PassTLSClientCert holds the pass TLS client cert middleware
|
||||
configuration. This middleware adds the selected data from the passed
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/passtlsclientcert/'
|
||||
client TLS certificate to a header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/'
|
||||
properties:
|
||||
info:
|
||||
description: Info selects the specific client certificate details
|
||||
|
@ -1305,7 +1295,7 @@ spec:
|
|||
rateLimit:
|
||||
description: 'RateLimit holds the rate limit configuration. This middleware
|
||||
ensures that services will receive a fair amount of requests, and
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ratelimit/'
|
||||
allows one to define what fair is. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/'
|
||||
properties:
|
||||
average:
|
||||
description: Average is the maximum rate, by default in requests/s,
|
||||
|
@ -1338,7 +1328,7 @@ spec:
|
|||
properties:
|
||||
ipStrategy:
|
||||
description: 'IPStrategy holds the IP strategy configuration
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy'
|
||||
used by Traefik to determine the client IP. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy'
|
||||
properties:
|
||||
depth:
|
||||
description: Depth tells Traefik to use the X-Forwarded-For
|
||||
|
@ -1366,7 +1356,7 @@ spec:
|
|||
redirectRegex:
|
||||
description: 'RedirectRegex holds the redirect regex middleware configuration.
|
||||
This middleware redirects a request using regex matching and replacement.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectregex/#regex'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -1384,7 +1374,7 @@ spec:
|
|||
redirectScheme:
|
||||
description: 'RedirectScheme holds the redirect scheme middleware
|
||||
configuration. This middleware redirects requests from a scheme/port
|
||||
to another. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectscheme/'
|
||||
to another. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/'
|
||||
properties:
|
||||
permanent:
|
||||
description: Permanent defines whether the redirection is permanent
|
||||
|
@ -1400,7 +1390,7 @@ spec:
|
|||
replacePath:
|
||||
description: 'ReplacePath holds the replace path middleware configuration.
|
||||
This middleware replaces the path of the request URL and store the
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepath/'
|
||||
original path in an X-Replaced-Path header. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/'
|
||||
properties:
|
||||
path:
|
||||
description: Path defines the path to use as replacement in the
|
||||
|
@ -1410,7 +1400,7 @@ spec:
|
|||
replacePathRegex:
|
||||
description: 'ReplacePathRegex holds the replace path regex middleware
|
||||
configuration. This middleware replaces the path of a URL using
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepathregex/'
|
||||
regex matching and replacement. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression used to match
|
||||
|
@ -1426,7 +1416,7 @@ spec:
|
|||
middleware reissues requests a given number of times to a backend
|
||||
server if that server does not reply. As soon as the server answers,
|
||||
the middleware stops retrying, regardless of the response status.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/retry/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/'
|
||||
properties:
|
||||
attempts:
|
||||
description: Attempts defines how many times the request should
|
||||
|
@ -1446,7 +1436,7 @@ spec:
|
|||
stripPrefix:
|
||||
description: 'StripPrefix holds the strip prefix middleware configuration.
|
||||
This middleware removes the specified prefixes from the URL path.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefix/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/'
|
||||
properties:
|
||||
prefixes:
|
||||
description: Prefixes defines the prefixes to strip from the request
|
||||
|
@ -1458,7 +1448,7 @@ spec:
|
|||
stripPrefixRegex:
|
||||
description: 'StripPrefixRegex holds the strip prefix regex middleware
|
||||
configuration. This middleware removes the matching prefixes from
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefixregex/'
|
||||
the URL path. More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/'
|
||||
properties:
|
||||
regex:
|
||||
description: Regex defines the regular expression to match the
|
||||
|
@ -1502,7 +1492,7 @@ spec:
|
|||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'MiddlewareTCP is the CRD implementation of a Traefik TCP middleware.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/middlewares/overview/'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/middlewares/overview/'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1576,7 +1566,7 @@ spec:
|
|||
description: 'ServersTransport is the CRD implementation of a ServersTransport.
|
||||
If no serversTransport is specified, the default@internal will be used.
|
||||
The default@internal serversTransport is created from the static configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#serverstransport_1'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_1'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1716,7 +1706,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TLSOption is the CRD implementation of a Traefik TLS Option,
|
||||
allowing to configure some parameters of the TLS connection. More info:
|
||||
https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options'
|
||||
https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1736,13 +1726,13 @@ spec:
|
|||
alpnProtocols:
|
||||
description: 'ALPNProtocols defines the list of supported application
|
||||
level protocols for the TLS handshake, in order of preference. More
|
||||
info: https://doc.traefik.io/traefik/v2.9/https/tls/#alpn-protocols'
|
||||
info: https://doc.traefik.io/traefik/v3.0/https/tls/#alpn-protocols'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cipherSuites:
|
||||
description: 'CipherSuites defines the list of supported cipher suites
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#cipher-suites'
|
||||
for TLS versions up to TLS 1.2. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#cipher-suites'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -1769,7 +1759,7 @@ spec:
|
|||
type: object
|
||||
curvePreferences:
|
||||
description: 'CurvePreferences defines the preferred elliptic curves
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#curve-preferences'
|
||||
in a specific order. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#curve-preferences'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -1824,7 +1814,7 @@ spec:
|
|||
description: 'TLSStore is the CRD implementation of a Traefik TLS Store. For
|
||||
the time being, only the TLSStore named default is supported. This means
|
||||
that you cannot have two stores that are named default in different Kubernetes
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v2.9/https/tls/#certificates-stores'
|
||||
namespaces. More info: https://doc.traefik.io/traefik/v3.0/https/tls/#certificates-stores'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -1922,7 +1912,7 @@ spec:
|
|||
openAPIV3Schema:
|
||||
description: 'TraefikService is the CRD implementation of a Traefik Service.
|
||||
TraefikService object allows to: - Apply weight to Services on load-balancing
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
- Mirror traffic on services More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-traefikservice'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -2021,7 +2011,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2105,7 +2095,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2205,7 +2195,7 @@ spec:
|
|||
type: string
|
||||
sticky:
|
||||
description: 'Sticky defines the sticky sessions configuration.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
@ -2244,7 +2234,7 @@ spec:
|
|||
type: array
|
||||
sticky:
|
||||
description: 'Sticky defines whether sticky sessions are enabled.
|
||||
More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#stickiness-and-load-balancing'
|
||||
properties:
|
||||
cookie:
|
||||
description: Cookie defines the sticky cookie configuration.
|
||||
|
|
|
@ -21,32 +21,12 @@
|
|||
[http.routers]
|
||||
[http.routers.router1]
|
||||
service = "service1"
|
||||
rule = "PathPrefix(`/css/ct/nomiddleware`) || PathPrefix(`/pdf/ct/nomiddleware`)"
|
||||
rule = "PathPrefix(`/`)"
|
||||
|
||||
[http.routers.router2]
|
||||
service = "service1"
|
||||
middlewares = ["autodetect"]
|
||||
rule = "PathPrefix(`/css/ct/middlewareauto`) || PathPrefix(`/pdf/ct/middlewareauto`)"
|
||||
|
||||
[http.routers.router3]
|
||||
service = "service1"
|
||||
middlewares = ["noautodetect"]
|
||||
rule = "PathPrefix(`/css/ct/middlewarenoauto`) || PathPrefix(`/pdf/ct/middlewarenoauto`)"
|
||||
|
||||
[http.routers.router4]
|
||||
service = "service1"
|
||||
rule = "PathPrefix(`/css/noct/nomiddleware`) || PathPrefix(`/pdf/noct/nomiddleware`)"
|
||||
|
||||
[http.routers.router5]
|
||||
service = "service1"
|
||||
middlewares = ["autodetect"]
|
||||
rule = "PathPrefix(`/css/noct/middlewareauto`) || PathPrefix(`/pdf/noct/middlewareauto`)"
|
||||
|
||||
[http.routers.router6]
|
||||
service = "service1"
|
||||
middlewares = ["noautodetect"]
|
||||
rule = "PathPrefix(`/css/noct/middlewarenoauto`) || PathPrefix(`/pdf/noct/middlewarenoauto`)"
|
||||
|
||||
rule = "PathPrefix(`/autodetect`)"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1]
|
||||
|
@ -56,7 +36,3 @@
|
|||
url = "{{ .Server }}"
|
||||
|
||||
[http.middlewares.autodetect.contentType]
|
||||
autoDetect=true
|
||||
|
||||
[http.middlewares.noautodetect.contentType]
|
||||
autoDetect=false
|
||||
|
|
15
integration/fixtures/simple_debug_log.toml
Normal file
15
integration/fixtures/simple_debug_log.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.docker]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.webHost]
|
||||
address = ":8000"
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
noColor = true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.webHost]
|
||||
|
@ -30,12 +31,12 @@
|
|||
[http.routers.router2]
|
||||
entryPoints = ["webHostRegexp"]
|
||||
service = "service1"
|
||||
rule = "!HostRegexp(`test.localhost`)"
|
||||
rule = "!HostRegexp(`test\\.localhost`)"
|
||||
|
||||
[http.routers.router3]
|
||||
entryPoints = ["webQuery"]
|
||||
service = "service1"
|
||||
rule = "!Query(`foo=`)"
|
||||
rule = "!QueryRegexp(`foo`, `.*`)"
|
||||
|
||||
|
||||
[http.services]
|
||||
|
|
|
@ -34,6 +34,13 @@
|
|||
[tcp.routers.to-whoami-sni-strict.tls]
|
||||
options = "bar"
|
||||
|
||||
[tcp.routers.to-whoami-invalid-tls]
|
||||
rule = "HostSNI(`whoami-i.test`)"
|
||||
service = "whoami-no-cert"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-invalid-tls.tls]
|
||||
options = "invalid"
|
||||
|
||||
[tcp.services.whoami-no-cert]
|
||||
[tcp.services.whoami-no-cert.loadBalancer]
|
||||
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
|
||||
|
@ -46,3 +53,7 @@
|
|||
|
||||
[tls.options.bar]
|
||||
minVersion = "VersionTLS13"
|
||||
|
||||
[tls.options.invalid.clientAuth]
|
||||
# Missing CA files to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
[http.routers]
|
||||
[http.routers.router1]
|
||||
service = "service1"
|
||||
rule = "Path(`/echo`,`/ws`)"
|
||||
rule = "Path(`/echo`) || Path(`/ws`)"
|
||||
[http.routers.router1.tls]
|
||||
|
||||
[http.services]
|
||||
|
|
|
@ -1092,7 +1092,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicCaseInsensitive(c *check.C) {
|
|||
defer s.killCmd(cmd)
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("HostRegexp(`{subdomain:[a-z1-9-]+}.www.snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("HostRegexp(`[a-z1-9-]+\\\\.www\\\\.snitest\\\\.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -1226,3 +1226,53 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
|
||||
func (s *HTTPSSuite) TestWithInvalidTLSOption(c *check.C) {
|
||||
backend := startTestServer("9010", http.StatusOK, "server1")
|
||||
defer backend.Close()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/https/https_invalid_tls_options.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serverName string
|
||||
}{
|
||||
{
|
||||
desc: "With invalid TLS Options specified",
|
||||
serverName: "snitest.com",
|
||||
},
|
||||
{
|
||||
desc: "With invalid Default TLS Options",
|
||||
serverName: "snitest.org",
|
||||
},
|
||||
{
|
||||
desc: "With TLS Options without servername (fallback to default)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
if test.serverName != "" {
|
||||
tlsConfig.ServerName = test.serverName
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("connected to server successfully"))
|
||||
c.Assert(conn, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
@ -1166,9 +1167,10 @@ func (s *SimpleSuite) TestSecureAPI(c *check.C) {
|
|||
func (s *SimpleSuite) TestContentTypeDisableAutoDetect(c *check.C) {
|
||||
srv1 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header()["Content-Type"] = nil
|
||||
switch req.URL.Path[:4] {
|
||||
path := strings.TrimPrefix(req.URL.Path, "/autodetect")
|
||||
switch path[:4] {
|
||||
case "/css":
|
||||
if !strings.Contains(req.URL.Path, "noct") {
|
||||
if strings.Contains(req.URL.Path, "/ct") {
|
||||
rw.Header().Set("Content-Type", "text/css")
|
||||
}
|
||||
|
||||
|
@ -1177,7 +1179,7 @@ func (s *SimpleSuite) TestContentTypeDisableAutoDetect(c *check.C) {
|
|||
_, err := rw.Write([]byte(".testcss { }"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
case "/pdf":
|
||||
if !strings.Contains(req.URL.Path, "noct") {
|
||||
if strings.Contains(req.URL.Path, "/ct") {
|
||||
rw.Header().Set("Content-Type", "application/pdf")
|
||||
}
|
||||
|
||||
|
@ -1211,37 +1213,13 @@ func (s *SimpleSuite) TestContentTypeDisableAutoDetect(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/ct/nomiddleware", time.Second, try.HasHeaderValue("Content-Type", "text/css", false))
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/ct", time.Second, try.HasHeaderValue("Content-Type", "text/css", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/ct/nomiddleware", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/ct", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/ct/middlewareauto", time.Second, try.HasHeaderValue("Content-Type", "text/css", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/ct/nomiddlewareauto", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/ct/middlewarenoauto", time.Second, try.HasHeaderValue("Content-Type", "text/css", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/ct/nomiddlewarenoauto", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/noct/nomiddleware", time.Second, try.HasHeaderValue("Content-Type", "text/plain; charset=utf-8", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/noct/nomiddleware", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/noct/middlewareauto", time.Second, try.HasHeaderValue("Content-Type", "text/plain; charset=utf-8", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/noct/nomiddlewareauto", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/noct/middlewarenoauto", time.Second, func(res *http.Response) error {
|
||||
err = try.GetRequest("http://127.0.0.1:8000/css/noct", time.Second, func(res *http.Response) error {
|
||||
if ct, ok := res.Header["Content-Type"]; ok {
|
||||
return fmt.Errorf("should have no content type and %s is present", ct)
|
||||
}
|
||||
|
@ -1249,13 +1227,25 @@ func (s *SimpleSuite) TestContentTypeDisableAutoDetect(c *check.C) {
|
|||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/noct/middlewarenoauto", time.Second, func(res *http.Response) error {
|
||||
err = try.GetRequest("http://127.0.0.1:8000/pdf/noct", time.Second, func(res *http.Response) error {
|
||||
if ct, ok := res.Header["Content-Type"]; ok {
|
||||
return fmt.Errorf("should have no content type and %s is present", ct)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/autodetect/css/ct", time.Second, try.HasHeaderValue("Content-Type", "text/css", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/autodetect/pdf/ct", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/autodetect/css/noct", time.Second, try.HasHeaderValue("Content-Type", "text/plain; charset=utf-8", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/autodetect/pdf/noct", time.Second, try.HasHeaderValue("Content-Type", "application/pdf", false))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestMuxer(c *check.C) {
|
||||
|
@ -1337,7 +1327,7 @@ func (s *SimpleSuite) TestMuxer(c *check.C) {
|
|||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "!Query with semicolon, no match",
|
||||
desc: "!Query with semicolon and empty query param value, no match",
|
||||
request: "GET /?foo=; HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8002",
|
||||
expected: http.StatusNotFound,
|
||||
|
@ -1367,9 +1357,7 @@ func (s *SimpleSuite) TestMuxer(c *check.C) {
|
|||
resp, err := http.ReadResponse(bufio.NewReader(conn), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
if resp.StatusCode != test.expected {
|
||||
c.Errorf("%s failed with %d instead of %d", test.desc, resp.StatusCode, test.expected)
|
||||
}
|
||||
c.Assert(resp.StatusCode, checker.Equals, test.expected, check.Commentf(test.desc))
|
||||
|
||||
if test.body != "" {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
|
@ -1378,3 +1366,36 @@ func (s *SimpleSuite) TestMuxer(c *check.C) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestDebugLog(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
s.composeUp(c)
|
||||
defer s.composeDown(c)
|
||||
|
||||
file := s.adaptFile(c, "fixtures/simple_debug_log.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, output := s.cmdTraefik(withConfigFile(file))
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/whoami`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:8000/whoami", http.NoBody)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Header.Set("Autorization", "Bearer ThisIsABearerToken")
|
||||
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
if regexp.MustCompile("ThisIsABearerToken").MatchReader(output) {
|
||||
c.Logf("Traefik Logs: %s", output.String())
|
||||
c.Log("Found Authorization Header in Traefik DEBUG logs")
|
||||
c.Fail()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,14 @@ func (s *TCPSuite) TestTLSOptions(c *check.C) {
|
|||
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
||||
|
||||
// Check that we can't reach a route with an invalid mTLS configuration.
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
|
||||
ServerName: "whoami-i.test",
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
c.Assert(conn, checker.IsNil)
|
||||
c.Assert(err, checker.NotNil)
|
||||
}
|
||||
|
||||
func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
|
||||
|
|
|
@ -33,7 +33,7 @@ type Middleware struct {
|
|||
Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||
PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty" export:"true"`
|
||||
Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty" export:"true"`
|
||||
ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty" export:"true"`
|
||||
ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||
GrpcWeb *GrpcWeb `json:"grpcWeb,omitempty" toml:"grpcWeb,omitempty" yaml:"grpcWeb,omitempty" export:"true"`
|
||||
|
||||
Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty" export:"true"`
|
||||
|
@ -52,21 +52,15 @@ type GrpcWeb struct {
|
|||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ContentType holds the content-type middleware configuration.
|
||||
// This middleware exists to enable the correct behavior until at least the default one can be changed in a future version.
|
||||
type ContentType struct {
|
||||
// AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
|
||||
// be automatically set to a value derived from the contents of the response.
|
||||
// As a proxy, the default behavior should be to leave the header alone, regardless of what the backend did with it.
|
||||
// However, the historic default was to always auto-detect and set the header if it was nil,
|
||||
// and it is going to be kept that way in order to support users currently relying on it.
|
||||
AutoDetect bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty" export:"true"`
|
||||
}
|
||||
// This middleware sets the `Content-Type` header value to the media type detected from the response content,
|
||||
// when it is not set by the backend.
|
||||
type ContentType struct{}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// AddPrefix holds the add prefix middleware configuration.
|
||||
// This middleware updates the path of a request before forwarding it.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/addprefix/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/
|
||||
type AddPrefix struct {
|
||||
// Prefix is the string to add before the current path in the requested URL.
|
||||
// It should include a leading slash (/).
|
||||
|
@ -77,7 +71,7 @@ type AddPrefix struct {
|
|||
|
||||
// BasicAuth holds the basic auth middleware configuration.
|
||||
// This middleware restricts access to your services to known users.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/
|
||||
type BasicAuth struct {
|
||||
// Users is an array of authorized users.
|
||||
// Each user must be declared using the name:hashed-password format.
|
||||
|
@ -92,7 +86,7 @@ type BasicAuth struct {
|
|||
// Default: false.
|
||||
RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty" export:"true"`
|
||||
// HeaderField defines a header field to store the authenticated user.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield
|
||||
HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
|
@ -100,7 +94,7 @@ type BasicAuth struct {
|
|||
|
||||
// Buffering holds the buffering middleware configuration.
|
||||
// This middleware retries or limits the size of requests that can be forwarded to backends.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#maxrequestbodybytes
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes
|
||||
type Buffering struct {
|
||||
// MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes).
|
||||
// If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response.
|
||||
|
@ -118,7 +112,7 @@ type Buffering struct {
|
|||
MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty" toml:"memResponseBodyBytes,omitempty" yaml:"memResponseBodyBytes,omitempty" export:"true"`
|
||||
// RetryExpression defines the retry conditions.
|
||||
// It is a logical combination of functions with operators AND (&&) and OR (||).
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/buffering/#retryexpression
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression
|
||||
RetryExpression string `json:"retryExpression,omitempty" toml:"retryExpression,omitempty" yaml:"retryExpression,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
|
@ -135,7 +129,7 @@ type Chain struct {
|
|||
|
||||
// CircuitBreaker holds the circuit breaker middleware configuration.
|
||||
// This middleware protects the system from stacking requests to unhealthy services, resulting in cascading failures.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/circuitbreaker/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/circuitbreaker/
|
||||
type CircuitBreaker struct {
|
||||
// Expression defines the expression that, once matched, opens the circuit breaker and applies the fallback mechanism instead of calling the services.
|
||||
Expression string `json:"expression,omitempty" toml:"expression,omitempty" yaml:"expression,omitempty" export:"true"`
|
||||
|
@ -158,7 +152,7 @@ func (c *CircuitBreaker) SetDefaults() {
|
|||
|
||||
// Compress holds the compress middleware configuration.
|
||||
// This middleware compresses responses before sending them to the client, using gzip compression.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/compress/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/
|
||||
type Compress struct {
|
||||
// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
||||
// `application/grpc` is always excluded.
|
||||
|
@ -172,7 +166,7 @@ type Compress struct {
|
|||
|
||||
// DigestAuth holds the digest auth middleware configuration.
|
||||
// This middleware restricts access to your services to known users.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/digestauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/
|
||||
type DigestAuth struct {
|
||||
// Users defines the authorized users.
|
||||
// Each user should be declared using the name:realm:encoded-password format.
|
||||
|
@ -185,7 +179,7 @@ type DigestAuth struct {
|
|||
// Default: traefik.
|
||||
Realm string `json:"realm,omitempty" toml:"realm,omitempty" yaml:"realm,omitempty"`
|
||||
// HeaderField defines a header field to store the authenticated user.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield
|
||||
HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
|
@ -211,7 +205,7 @@ type ErrorPage struct {
|
|||
|
||||
// ForwardAuth holds the forward auth middleware configuration.
|
||||
// This middleware delegates the request authentication to a Service.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/
|
||||
type ForwardAuth struct {
|
||||
// Address defines the authentication server address.
|
||||
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
|
@ -222,7 +216,7 @@ type ForwardAuth struct {
|
|||
// AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers.
|
||||
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"`
|
||||
// AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/#authresponseheadersregex
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex
|
||||
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"`
|
||||
// AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server.
|
||||
// If not set or empty then all request headers are passed.
|
||||
|
@ -233,7 +227,7 @@ type ForwardAuth struct {
|
|||
|
||||
// Headers holds the headers middleware configuration.
|
||||
// This middleware manages the requests and responses headers.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/headers/#customrequestheaders
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders
|
||||
type Headers struct {
|
||||
// CustomRequestHeaders defines the header names and values to apply to the request.
|
||||
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty" toml:"customRequestHeaders,omitempty" yaml:"customRequestHeaders,omitempty" export:"true"`
|
||||
|
@ -343,7 +337,7 @@ func (h *Headers) HasSecureHeadersDefined() bool {
|
|||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/#ipstrategy
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy
|
||||
type IPStrategy struct {
|
||||
// Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right).
|
||||
Depth int `json:"depth,omitempty" toml:"depth,omitempty" yaml:"depth,omitempty" export:"true"`
|
||||
|
@ -384,7 +378,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
|
|||
|
||||
// IPAllowList holds the IP allowlist middleware configuration.
|
||||
// This middleware accepts / refuses requests based on the client IP.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ipallowlist/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/
|
||||
type IPAllowList struct {
|
||||
// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation).
|
||||
SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"`
|
||||
|
@ -395,7 +389,7 @@ type IPAllowList struct {
|
|||
|
||||
// InFlightReq holds the in-flight request middleware configuration.
|
||||
// This middleware limits the number of requests being processed and served concurrently.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/
|
||||
type InFlightReq struct {
|
||||
// Amount defines the maximum amount of allowed simultaneous in-flight request.
|
||||
// The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy).
|
||||
|
@ -403,7 +397,7 @@ type InFlightReq struct {
|
|||
// SourceCriterion defines what criterion is used to group requests as originating from a common source.
|
||||
// If several strategies are defined at the same time, an error will be raised.
|
||||
// If none are set, the default is to use the requestHost.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/inflightreq/#sourcecriterion
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion
|
||||
SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
|
@ -411,7 +405,7 @@ type InFlightReq struct {
|
|||
|
||||
// PassTLSClientCert holds the pass TLS client cert middleware configuration.
|
||||
// This middleware adds the selected data from the passed client TLS certificate to a header.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/passtlsclientcert/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/
|
||||
type PassTLSClientCert struct {
|
||||
// PEM sets the X-Forwarded-Tls-Client-Cert header with the certificate.
|
||||
PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty" export:"true"`
|
||||
|
@ -467,7 +461,7 @@ func (r *RateLimit) SetDefaults() {
|
|||
|
||||
// RedirectRegex holds the redirect regex middleware configuration.
|
||||
// This middleware redirects a request using regex matching and replacement.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectregex/#regex
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex
|
||||
type RedirectRegex struct {
|
||||
// Regex defines the regex used to match and capture elements from the request URL.
|
||||
Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"`
|
||||
|
@ -481,7 +475,7 @@ type RedirectRegex struct {
|
|||
|
||||
// RedirectScheme holds the redirect scheme middleware configuration.
|
||||
// This middleware redirects requests from a scheme/port to another.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/redirectscheme/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/
|
||||
type RedirectScheme struct {
|
||||
// Scheme defines the scheme of the new URL.
|
||||
Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"`
|
||||
|
@ -495,7 +489,7 @@ type RedirectScheme struct {
|
|||
|
||||
// ReplacePath holds the replace path middleware configuration.
|
||||
// This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepath/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/
|
||||
type ReplacePath struct {
|
||||
// Path defines the path to use as replacement in the request URL.
|
||||
Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"`
|
||||
|
@ -505,7 +499,7 @@ type ReplacePath struct {
|
|||
|
||||
// ReplacePathRegex holds the replace path regex middleware configuration.
|
||||
// This middleware replaces the path of a URL using regex matching and replacement.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/replacepathregex/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/
|
||||
type ReplacePathRegex struct {
|
||||
// Regex defines the regular expression used to match and capture the path from the request URL.
|
||||
Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"`
|
||||
|
@ -518,7 +512,7 @@ type ReplacePathRegex struct {
|
|||
// Retry holds the retry middleware configuration.
|
||||
// This middleware reissues requests a given number of times to a backend server if that server does not reply.
|
||||
// As soon as the server answers, the middleware stops retrying, regardless of the response status.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/retry/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/
|
||||
type Retry struct {
|
||||
// Attempts defines how many times the request should be retried.
|
||||
Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty" export:"true"`
|
||||
|
@ -534,7 +528,7 @@ type Retry struct {
|
|||
|
||||
// StripPrefix holds the strip prefix middleware configuration.
|
||||
// This middleware removes the specified prefixes from the URL path.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefix/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/
|
||||
type StripPrefix struct {
|
||||
// Prefixes defines the prefixes to strip from the request URL.
|
||||
Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty" export:"true"`
|
||||
|
@ -544,7 +538,7 @@ type StripPrefix struct {
|
|||
|
||||
// StripPrefixRegex holds the strip prefix regex middleware configuration.
|
||||
// This middleware removes the matching prefixes from the URL path.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/stripprefixregex/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/
|
||||
type StripPrefixRegex struct {
|
||||
// Regex defines the regular expression to match the path prefix from the request URL.
|
||||
Regex []string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"`
|
||||
|
|
|
@ -114,7 +114,7 @@ type TCPServer struct {
|
|||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ProxyProtocol holds the PROXY Protocol configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol
|
||||
type ProxyProtocol struct {
|
||||
// Version defines the PROXY Protocol version to use.
|
||||
Version int `json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"`
|
||||
|
|
|
@ -13,7 +13,7 @@ type TCPMiddleware struct {
|
|||
// TCPInFlightConn holds the TCP InFlightConn middleware configuration.
|
||||
// This middleware prevents services from being overwhelmed with high load,
|
||||
// by limiting the number of allowed simultaneous connections for one IP.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/tcp/inflightconn/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/inflightconn/
|
||||
type TCPInFlightConn struct {
|
||||
// Amount defines the maximum amount of allowed simultaneous connections.
|
||||
// The middleware closes the connection if there are already amount connections opened.
|
||||
|
|
|
@ -8,6 +8,5 @@ type Experimental struct {
|
|||
LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"`
|
||||
|
||||
KubernetesGateway bool `description:"Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
|
||||
HTTP3 bool `description:"Enable HTTP3." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" export:"true"`
|
||||
Hub bool `description:"Enable the Traefik Hub provider." json:"hub,omitempty" toml:"hub,omitempty" yaml:"hub,omitempty" export:"true"`
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/tracing/haystack"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/instana"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/opentelemetry"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/zipkin"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
@ -169,14 +170,15 @@ func (a *LifeCycle) SetDefaults() {
|
|||
|
||||
// Tracing holds the tracing configuration.
|
||||
type Tracing struct {
|
||||
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)." json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty" export:"true"`
|
||||
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)." json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty" export:"true"`
|
||||
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
OpenTelemetry *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"openTelemetry,omitempty" toml:"openTelemetry,omitempty" yaml:"openTelemetry,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
|
@ -266,16 +268,6 @@ func (c *Configuration) SetEffectiveConfiguration() {
|
|||
c.Providers.KubernetesGateway = nil
|
||||
}
|
||||
|
||||
if c.Experimental == nil || !c.Experimental.HTTP3 {
|
||||
for epName, ep := range c.EntryPoints {
|
||||
if ep.HTTP3 != nil {
|
||||
ep.HTTP3 = nil
|
||||
log.Debug().Str(logs.EntryPointName, epName).
|
||||
Msgf("Disabling HTTP3 configuration for entryPoint %q: HTTP3 is disabled in the experimental configuration section", epName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Gateway API provider
|
||||
if c.Providers.KubernetesGateway != nil {
|
||||
log.Debug().Msg("Experimental Kubernetes Gateway provider has been activated")
|
||||
|
|
422
pkg/metrics/opentelemetry.go
Normal file
422
pkg/metrics/opentelemetry.go
Normal file
|
@ -0,0 +1,422 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/global"
|
||||
"go.opentelemetry.io/otel/metric/instrument"
|
||||
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
|
||||
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
|
||||
"go.opentelemetry.io/otel/metric/unit"
|
||||
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregation"
|
||||
"go.opentelemetry.io/otel/sdk/metric/view"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
)
|
||||
|
||||
var (
|
||||
openTelemetryMeterProvider *sdkmetric.MeterProvider
|
||||
openTelemetryGaugeCollector *gaugeCollector
|
||||
)
|
||||
|
||||
// RegisterOpenTelemetry registers all OpenTelemetry metrics.
|
||||
func RegisterOpenTelemetry(ctx context.Context, config *types.OpenTelemetry) Registry {
|
||||
if openTelemetryMeterProvider == nil {
|
||||
var err error
|
||||
if openTelemetryMeterProvider, err = newOpenTelemetryMeterProvider(ctx, config); err != nil {
|
||||
log.Ctx(ctx).Err(err).Msg("Unable to create OpenTelemetry meter provider")
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if openTelemetryGaugeCollector == nil {
|
||||
openTelemetryGaugeCollector = newOpenTelemetryGaugeCollector()
|
||||
}
|
||||
|
||||
meter := global.Meter("github.com/traefik/traefik",
|
||||
metric.WithInstrumentationVersion(version.Version))
|
||||
|
||||
reg := &standardRegistry{
|
||||
epEnabled: config.AddEntryPointsLabels,
|
||||
routerEnabled: config.AddRoutersLabels,
|
||||
svcEnabled: config.AddServicesLabels,
|
||||
configReloadsCounter: newOTLPCounterFrom(meter, configReloadsTotalName, "Config reloads"),
|
||||
configReloadsFailureCounter: newOTLPCounterFrom(meter, configReloadsFailuresTotalName, "Config reload failures"),
|
||||
lastConfigReloadSuccessGauge: newOTLPGaugeFrom(meter, configLastReloadSuccessName, "Last config reload success", unit.Milliseconds),
|
||||
lastConfigReloadFailureGauge: newOTLPGaugeFrom(meter, configLastReloadFailureName, "Last config reload failure", unit.Milliseconds),
|
||||
tlsCertsNotAfterTimestampGauge: newOTLPGaugeFrom(meter, tlsCertsNotAfterTimestamp, "Certificate expiration timestamp", unit.Milliseconds),
|
||||
}
|
||||
|
||||
if config.AddEntryPointsLabels {
|
||||
reg.entryPointReqsCounter = newOTLPCounterFrom(meter, entryPointReqsTotalName,
|
||||
"How many HTTP requests processed on an entrypoint, partitioned by status code, protocol, and method.")
|
||||
reg.entryPointReqsTLSCounter = newOTLPCounterFrom(meter, entryPointReqsTLSTotalName,
|
||||
"How many HTTP requests with TLS processed on an entrypoint, partitioned by TLS Version and TLS cipher Used.")
|
||||
reg.entryPointReqDurationHistogram, _ = NewHistogramWithScale(newOTLPHistogramFrom(meter, entryPointReqDurationName,
|
||||
"How long it took to process the request on an entrypoint, partitioned by status code, protocol, and method.",
|
||||
unit.Milliseconds), time.Second)
|
||||
reg.entryPointOpenConnsGauge = newOTLPGaugeFrom(meter, entryPointOpenConnsName,
|
||||
"How many open connections exist on an entrypoint, partitioned by method and protocol.",
|
||||
unit.Dimensionless)
|
||||
}
|
||||
|
||||
if config.AddRoutersLabels {
|
||||
reg.routerReqsCounter = newOTLPCounterFrom(meter, routerReqsTotalName,
|
||||
"How many HTTP requests are processed on a router, partitioned by service, status code, protocol, and method.")
|
||||
reg.routerReqsTLSCounter = newOTLPCounterFrom(meter, routerReqsTLSTotalName,
|
||||
"How many HTTP requests with TLS are processed on a router, partitioned by service, TLS Version, and TLS cipher Used.")
|
||||
reg.routerReqDurationHistogram, _ = NewHistogramWithScale(newOTLPHistogramFrom(meter, routerReqDurationName,
|
||||
"How long it took to process the request on a router, partitioned by service, status code, protocol, and method.",
|
||||
unit.Milliseconds), time.Second)
|
||||
reg.routerOpenConnsGauge = newOTLPGaugeFrom(meter, routerOpenConnsName,
|
||||
"How many open connections exist on a router, partitioned by service, method, and protocol.",
|
||||
unit.Dimensionless)
|
||||
}
|
||||
|
||||
if config.AddServicesLabels {
|
||||
reg.serviceReqsCounter = newOTLPCounterFrom(meter, serviceReqsTotalName,
|
||||
"How many HTTP requests processed on a service, partitioned by status code, protocol, and method.")
|
||||
reg.serviceReqsTLSCounter = newOTLPCounterFrom(meter, serviceReqsTLSTotalName,
|
||||
"How many HTTP requests with TLS processed on a service, partitioned by TLS version and TLS cipher.")
|
||||
reg.serviceReqDurationHistogram, _ = NewHistogramWithScale(newOTLPHistogramFrom(meter, serviceReqDurationName,
|
||||
"How long it took to process the request on a service, partitioned by status code, protocol, and method.",
|
||||
unit.Milliseconds), time.Second)
|
||||
reg.serviceOpenConnsGauge = newOTLPGaugeFrom(meter, serviceOpenConnsName,
|
||||
"How many open connections exist on a service, partitioned by method and protocol.",
|
||||
unit.Dimensionless)
|
||||
reg.serviceRetriesCounter = newOTLPCounterFrom(meter, serviceRetriesTotalName,
|
||||
"How many request retries happened on a service.")
|
||||
reg.serviceServerUpGauge = newOTLPGaugeFrom(meter, serviceServerUpName,
|
||||
"service server is up, described by gauge value of 0 or 1.",
|
||||
unit.Dimensionless)
|
||||
}
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
// StopOpenTelemetry stops and resets Open-Telemetry client.
|
||||
func StopOpenTelemetry() {
|
||||
if openTelemetryMeterProvider == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := openTelemetryMeterProvider.Shutdown(ctx); err != nil {
|
||||
log.Err(err).Msg("Unable to shutdown OpenTelemetry meter provider")
|
||||
}
|
||||
|
||||
openTelemetryMeterProvider = nil
|
||||
}
|
||||
|
||||
// newOpenTelemetryMeterProvider creates a new controller.Controller.
|
||||
func newOpenTelemetryMeterProvider(ctx context.Context, config *types.OpenTelemetry) (*sdkmetric.MeterProvider, error) {
|
||||
var (
|
||||
exporter sdkmetric.Exporter
|
||||
err error
|
||||
)
|
||||
if config.GRPC != nil {
|
||||
exporter, err = newGRPCExporter(ctx, config)
|
||||
} else {
|
||||
exporter, err = newHTTPExporter(ctx, config)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating exporter: %w", err)
|
||||
}
|
||||
|
||||
opts := []sdkmetric.PeriodicReaderOption{
|
||||
sdkmetric.WithInterval(time.Duration(config.PushInterval)),
|
||||
}
|
||||
|
||||
// View to customize histogram buckets and rename a single histogram instrument.
|
||||
customBucketsView, err := view.New(
|
||||
// Match* to match instruments
|
||||
view.MatchInstrumentName("traefik_*_request_duration_seconds"),
|
||||
|
||||
view.WithSetAggregation(aggregation.ExplicitBucketHistogram{
|
||||
Boundaries: config.ExplicitBoundaries,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating histogram view: %w", err)
|
||||
}
|
||||
|
||||
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(
|
||||
sdkmetric.NewPeriodicReader(exporter, opts...),
|
||||
customBucketsView,
|
||||
))
|
||||
|
||||
global.SetMeterProvider(meterProvider)
|
||||
|
||||
return meterProvider, nil
|
||||
}
|
||||
|
||||
func newHTTPExporter(ctx context.Context, config *types.OpenTelemetry) (sdkmetric.Exporter, error) {
|
||||
host, port, err := net.SplitHostPort(config.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector address %q: %w", config.Address, err)
|
||||
}
|
||||
|
||||
opts := []otlpmetrichttp.Option{
|
||||
otlpmetrichttp.WithEndpoint(fmt.Sprintf("%s:%s", host, port)),
|
||||
otlpmetrichttp.WithHeaders(config.Headers),
|
||||
otlpmetrichttp.WithCompression(otlpmetrichttp.GzipCompression),
|
||||
}
|
||||
|
||||
if config.Insecure {
|
||||
opts = append(opts, otlpmetrichttp.WithInsecure())
|
||||
}
|
||||
|
||||
if config.Path != "" {
|
||||
opts = append(opts, otlpmetrichttp.WithURLPath(config.Path))
|
||||
}
|
||||
|
||||
if config.TLS != nil {
|
||||
tlsConfig, err := config.TLS.CreateTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlpmetrichttp.WithTLSClientConfig(tlsConfig))
|
||||
}
|
||||
|
||||
return otlpmetrichttp.New(ctx, opts...)
|
||||
}
|
||||
|
||||
func newGRPCExporter(ctx context.Context, config *types.OpenTelemetry) (sdkmetric.Exporter, error) {
|
||||
host, port, err := net.SplitHostPort(config.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector address %q: %w", config.Address, err)
|
||||
}
|
||||
|
||||
opts := []otlpmetricgrpc.Option{
|
||||
otlpmetricgrpc.WithEndpoint(fmt.Sprintf("%s:%s", host, port)),
|
||||
otlpmetricgrpc.WithHeaders(config.Headers),
|
||||
otlpmetricgrpc.WithCompressor(gzip.Name),
|
||||
}
|
||||
|
||||
if config.Insecure {
|
||||
opts = append(opts, otlpmetricgrpc.WithInsecure())
|
||||
}
|
||||
|
||||
if config.TLS != nil {
|
||||
tlsConfig, err := config.TLS.CreateTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
|
||||
}
|
||||
|
||||
return otlpmetricgrpc.New(ctx, opts...)
|
||||
}
|
||||
|
||||
func newOTLPCounterFrom(meter metric.Meter, name, desc string) *otelCounter {
|
||||
c, _ := meter.SyncFloat64().Counter(name,
|
||||
instrument.WithDescription(desc),
|
||||
instrument.WithUnit(unit.Dimensionless),
|
||||
)
|
||||
|
||||
return &otelCounter{
|
||||
ip: c,
|
||||
}
|
||||
}
|
||||
|
||||
type otelCounter struct {
|
||||
labelNamesValues otelLabelNamesValues
|
||||
ip syncfloat64.Counter
|
||||
}
|
||||
|
||||
func (c *otelCounter) With(labelValues ...string) metrics.Counter {
|
||||
return &otelCounter{
|
||||
labelNamesValues: c.labelNamesValues.With(labelValues...),
|
||||
ip: c.ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *otelCounter) Add(delta float64) {
|
||||
c.ip.Add(context.Background(), delta, c.labelNamesValues.ToLabels()...)
|
||||
}
|
||||
|
||||
type gaugeValue struct {
|
||||
attributes otelLabelNamesValues
|
||||
value float64
|
||||
}
|
||||
|
||||
type gaugeCollector struct {
|
||||
mu sync.Mutex
|
||||
values map[string]map[string]gaugeValue
|
||||
}
|
||||
|
||||
func newOpenTelemetryGaugeCollector() *gaugeCollector {
|
||||
return &gaugeCollector{
|
||||
values: make(map[string]map[string]gaugeValue),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gaugeCollector) add(name string, delta float64, attributes otelLabelNamesValues) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
str := strings.Join(attributes, "")
|
||||
|
||||
if _, exists := c.values[name]; !exists {
|
||||
c.values[name] = map[string]gaugeValue{
|
||||
str: {
|
||||
attributes: attributes,
|
||||
value: delta,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
v, exists := c.values[name][str]
|
||||
if !exists {
|
||||
c.values[name][str] = gaugeValue{
|
||||
attributes: attributes,
|
||||
value: delta,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.values[name][str] = gaugeValue{
|
||||
attributes: attributes,
|
||||
value: v.value + delta,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gaugeCollector) set(name string, value float64, attributes otelLabelNamesValues) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if _, exists := c.values[name]; !exists {
|
||||
c.values[name] = make(map[string]gaugeValue)
|
||||
}
|
||||
|
||||
c.values[name][strings.Join(attributes, "")] = gaugeValue{
|
||||
attributes: attributes,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func newOTLPGaugeFrom(meter metric.Meter, name, desc string, u unit.Unit) *otelGauge {
|
||||
openTelemetryGaugeCollector.values[name] = make(map[string]gaugeValue)
|
||||
|
||||
c, _ := meter.AsyncFloat64().Gauge(name,
|
||||
instrument.WithDescription(desc),
|
||||
instrument.WithUnit(u),
|
||||
)
|
||||
|
||||
err := meter.RegisterCallback([]instrument.Asynchronous{c}, func(ctx context.Context) {
|
||||
openTelemetryGaugeCollector.mu.Lock()
|
||||
defer openTelemetryGaugeCollector.mu.Unlock()
|
||||
|
||||
values, exists := openTelemetryGaugeCollector.values[name]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
c.Observe(ctx, value.value, value.attributes.ToLabels()...)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to register OpenTelemetry meter callback")
|
||||
}
|
||||
|
||||
return &otelGauge{
|
||||
ip: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
type otelGauge struct {
|
||||
labelNamesValues otelLabelNamesValues
|
||||
ip asyncfloat64.Gauge
|
||||
name string
|
||||
}
|
||||
|
||||
func (g *otelGauge) With(labelValues ...string) metrics.Gauge {
|
||||
return &otelGauge{
|
||||
labelNamesValues: g.labelNamesValues.With(labelValues...),
|
||||
ip: g.ip,
|
||||
name: g.name,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *otelGauge) Add(delta float64) {
|
||||
openTelemetryGaugeCollector.add(g.name, delta, g.labelNamesValues)
|
||||
}
|
||||
|
||||
func (g *otelGauge) Set(value float64) {
|
||||
openTelemetryGaugeCollector.set(g.name, value, g.labelNamesValues)
|
||||
}
|
||||
|
||||
func newOTLPHistogramFrom(meter metric.Meter, name, desc string, u unit.Unit) *otelHistogram {
|
||||
c, _ := meter.SyncFloat64().Histogram(name,
|
||||
instrument.WithDescription(desc),
|
||||
instrument.WithUnit(u),
|
||||
)
|
||||
|
||||
return &otelHistogram{
|
||||
ip: c,
|
||||
}
|
||||
}
|
||||
|
||||
type otelHistogram struct {
|
||||
labelNamesValues otelLabelNamesValues
|
||||
ip syncfloat64.Histogram
|
||||
}
|
||||
|
||||
func (h *otelHistogram) With(labelValues ...string) metrics.Histogram {
|
||||
return &otelHistogram{
|
||||
labelNamesValues: h.labelNamesValues.With(labelValues...),
|
||||
ip: h.ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *otelHistogram) Observe(incr float64) {
|
||||
h.ip.Record(context.Background(), incr, h.labelNamesValues.ToLabels()...)
|
||||
}
|
||||
|
||||
// otelLabelNamesValues is the equivalent of prometheus' labelNamesValues
|
||||
// but adapted to OpenTelemetry.
|
||||
// otelLabelNamesValues is a type alias that provides validation on its With
|
||||
// method.
|
||||
// Metrics may include it as a member to help them satisfy With semantics and
|
||||
// save some code duplication.
|
||||
type otelLabelNamesValues []string
|
||||
|
||||
// With validates the input, and returns a new aggregate otelLabelNamesValues.
|
||||
func (lvs otelLabelNamesValues) With(labelValues ...string) otelLabelNamesValues {
|
||||
if len(labelValues)%2 != 0 {
|
||||
labelValues = append(labelValues, "unknown")
|
||||
}
|
||||
return append(lvs, labelValues...)
|
||||
}
|
||||
|
||||
// ToLabels is a convenience method to convert a otelLabelNamesValues
|
||||
// to the native attribute.KeyValue.
|
||||
func (lvs otelLabelNamesValues) ToLabels() []attribute.KeyValue {
|
||||
labels := make([]attribute.KeyValue, len(lvs)/2)
|
||||
for i := 0; i < len(labels); i++ {
|
||||
labels[i] = attribute.String(lvs[2*i], lvs[2*i+1])
|
||||
}
|
||||
return labels
|
||||
}
|
446
pkg/metrics/opentelemetry_test.go
Normal file
446
pkg/metrics/opentelemetry_test.go
Normal file
|
@ -0,0 +1,446 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
func TestOpenTelemetry_labels(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
values otelLabelNamesValues
|
||||
with []string
|
||||
expect []attribute.KeyValue
|
||||
}{
|
||||
{
|
||||
desc: "with no starting value",
|
||||
values: otelLabelNamesValues{},
|
||||
expect: []attribute.KeyValue{},
|
||||
},
|
||||
{
|
||||
desc: "with one starting value",
|
||||
values: otelLabelNamesValues{"foo"},
|
||||
expect: []attribute.KeyValue{},
|
||||
},
|
||||
{
|
||||
desc: "with two starting value",
|
||||
values: otelLabelNamesValues{"foo", "bar"},
|
||||
expect: []attribute.KeyValue{attribute.String("foo", "bar")},
|
||||
},
|
||||
{
|
||||
desc: "with no starting value, and with one other value",
|
||||
values: otelLabelNamesValues{},
|
||||
with: []string{"baz"},
|
||||
expect: []attribute.KeyValue{attribute.String("baz", "unknown")},
|
||||
},
|
||||
{
|
||||
desc: "with no starting value, and with two other value",
|
||||
values: otelLabelNamesValues{},
|
||||
with: []string{"baz", "buz"},
|
||||
expect: []attribute.KeyValue{attribute.String("baz", "buz")},
|
||||
},
|
||||
{
|
||||
desc: "with one starting value, and with one other value",
|
||||
values: otelLabelNamesValues{"foo"},
|
||||
with: []string{"baz"},
|
||||
expect: []attribute.KeyValue{attribute.String("foo", "baz")},
|
||||
},
|
||||
{
|
||||
desc: "with one starting value, and with two other value",
|
||||
values: otelLabelNamesValues{"foo"},
|
||||
with: []string{"baz", "buz"},
|
||||
expect: []attribute.KeyValue{attribute.String("foo", "baz")},
|
||||
},
|
||||
{
|
||||
desc: "with two starting value, and with one other value",
|
||||
values: otelLabelNamesValues{"foo", "bar"},
|
||||
with: []string{"baz"},
|
||||
expect: []attribute.KeyValue{
|
||||
attribute.String("foo", "bar"),
|
||||
attribute.String("baz", "unknown"),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with two starting value, and with two other value",
|
||||
values: otelLabelNamesValues{"foo", "bar"},
|
||||
with: []string{"baz", "buz"},
|
||||
expect: []attribute.KeyValue{
|
||||
attribute.String("foo", "bar"),
|
||||
attribute.String("baz", "buz"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, test.expect, test.values.With(test.with...).ToLabels())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenTelemetry_GaugeCollectorAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
gc *gaugeCollector
|
||||
delta float64
|
||||
name string
|
||||
attributes otelLabelNamesValues
|
||||
expect map[string]map[string]gaugeValue
|
||||
}{
|
||||
{
|
||||
desc: "empty collector",
|
||||
gc: newOpenTelemetryGaugeCollector(),
|
||||
delta: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
delta: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 2}},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector, values with label (only the last one counts)",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"bar": {
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
delta: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"": {
|
||||
value: 1,
|
||||
},
|
||||
"bar": {
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector, values with label on set",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {"bar": {value: 1}},
|
||||
},
|
||||
},
|
||||
delta: 1,
|
||||
name: "foo",
|
||||
attributes: otelLabelNamesValues{"baz"},
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"bar": {
|
||||
value: 1,
|
||||
},
|
||||
"baz": {
|
||||
value: 1,
|
||||
attributes: otelLabelNamesValues{"baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
test.gc.add(test.name, test.delta, test.attributes)
|
||||
|
||||
assert.Equal(t, test.expect, test.gc.values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenTelemetry_GaugeCollectorSet(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
gc *gaugeCollector
|
||||
value float64
|
||||
name string
|
||||
attributes otelLabelNamesValues
|
||||
expect map[string]map[string]gaugeValue
|
||||
}{
|
||||
{
|
||||
desc: "empty collector",
|
||||
gc: newOpenTelemetryGaugeCollector(),
|
||||
value: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
value: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector, values with label",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"bar": {
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
value: 1,
|
||||
name: "foo",
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"": {
|
||||
value: 1,
|
||||
},
|
||||
"bar": {
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "initialized collector, values with label on set",
|
||||
gc: &gaugeCollector{
|
||||
values: map[string]map[string]gaugeValue{
|
||||
"foo": {"": {value: 1}},
|
||||
},
|
||||
},
|
||||
value: 1,
|
||||
name: "foo",
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
expect: map[string]map[string]gaugeValue{
|
||||
"foo": {
|
||||
"": {
|
||||
value: 1,
|
||||
},
|
||||
"bar": {
|
||||
value: 1,
|
||||
attributes: otelLabelNamesValues{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
test.gc.set(test.name, test.value, test.attributes)
|
||||
|
||||
assert.Equal(t, test.expect, test.gc.values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenTelemetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := make(chan *string)
|
||||
defer close(c)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gzr, err := gzip.NewReader(r.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(gzr)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := pmetricotlp.NewExportRequest()
|
||||
err = req.UnmarshalProto(body)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalledReq, err := json.Marshal(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
bodyStr := string(marshalledReq)
|
||||
c <- &bodyStr
|
||||
|
||||
_, err = fmt.Fprintln(w, "ok")
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
sURL, err := url.Parse(ts.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
var cfg types.OpenTelemetry
|
||||
(&cfg).SetDefaults()
|
||||
cfg.AddRoutersLabels = true
|
||||
cfg.Address = sURL.Host
|
||||
cfg.Insecure = true
|
||||
cfg.PushInterval = ptypes.Duration(10 * time.Millisecond)
|
||||
|
||||
registry := RegisterOpenTelemetry(context.Background(), &cfg)
|
||||
require.NotNil(t, registry)
|
||||
|
||||
if !registry.IsEpEnabled() || !registry.IsRouterEnabled() || !registry.IsSvcEnabled() {
|
||||
t.Fatalf("registry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()")
|
||||
}
|
||||
|
||||
// TODO: the len of startUnixNano is no supposed to be 20, it should be 19
|
||||
expectedServer := []string{
|
||||
`({"name":"traefik_config_reloads_total","description":"Config reloads","unit":"1","sum":{"dataPoints":\[{"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_config_reloads_failure_total","description":"Config reload failures","unit":"1","sum":{"dataPoints":\[{"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_config_last_reload_success","description":"Last config reload success","unit":"ms","gauge":{"dataPoints":\[{"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
`({"name":"traefik_config_last_reload_failure","description":"Last config reload failure","unit":"ms","gauge":{"dataPoints":\[{"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
}
|
||||
|
||||
registry.ConfigReloadsCounter().Add(1)
|
||||
registry.ConfigReloadsFailureCounter().Add(1)
|
||||
registry.LastConfigReloadSuccessGauge().Set(1)
|
||||
registry.LastConfigReloadFailureGauge().Set(1)
|
||||
msgServer := <-c
|
||||
|
||||
assertMessage(t, *msgServer, expectedServer)
|
||||
|
||||
expectedTLS := []string{
|
||||
`({"name":"traefik_tls_certs_not_after","description":"Certificate expiration timestamp","unit":"ms","gauge":{"dataPoints":\[{"attributes":\[{"key":"key","value":{"stringValue":"value"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
}
|
||||
|
||||
registry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1)
|
||||
msgTLS := <-c
|
||||
|
||||
assertMessage(t, *msgTLS, expectedTLS)
|
||||
|
||||
expectedEntrypoint := []string{
|
||||
`({"name":"traefik_entrypoint_requests_total","description":"How many HTTP requests processed on an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_entrypoint_requests_tls_total","description":"How many HTTP requests with TLS processed on an entrypoint, partitioned by TLS Version and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test2"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_entrypoint_request_duration_seconds","description":"How long it took to process the request on an entrypoint, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test3"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_entrypoint_open_connections","description":"How many open connections exist on an entrypoint, partitioned by method and protocol.","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test4"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
}
|
||||
|
||||
registry.EntryPointReqsCounter().With("entrypoint", "test1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
|
||||
registry.EntryPointReqsTLSCounter().With("entrypoint", "test2", "tls_version", "foo", "tls_cipher", "bar").Add(1)
|
||||
registry.EntryPointReqDurationHistogram().With("entrypoint", "test3").Observe(10000)
|
||||
registry.EntryPointOpenConnsGauge().With("entrypoint", "test4").Set(1)
|
||||
msgEntrypoint := <-c
|
||||
|
||||
assertMessage(t, *msgEntrypoint, expectedEntrypoint)
|
||||
|
||||
expectedRouter := []string{
|
||||
`({"name":"traefik_router_requests_total","description":"How many HTTP requests are processed on a router, partitioned by service, status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_router_requests_tls_total","description":"How many HTTP requests with TLS are processed on a router, partitioned by service, TLS Version, and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_router_request_duration_seconds","description":"How long it took to process the request on a router, partitioned by service, status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_router_open_connections","description":"How many open connections exist on a router, partitioned by service, method, and protocol.","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
}
|
||||
|
||||
registry.RouterReqsCounter().With("router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
|
||||
registry.RouterReqsCounter().With("router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
|
||||
registry.RouterReqsTLSCounter().With("router", "demo", "service", "test", "tls_version", "foo", "tls_cipher", "bar").Add(1)
|
||||
registry.RouterReqDurationHistogram().With("router", "demo", "service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000)
|
||||
registry.RouterOpenConnsGauge().With("router", "demo", "service", "test").Set(1)
|
||||
msgRouter := <-c
|
||||
|
||||
assertMessage(t, *msgRouter, expectedRouter)
|
||||
|
||||
expectedService := []string{
|
||||
`({"name":"traefik_service_requests_total","description":"How many HTTP requests processed on a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_service_requests_tls_total","description":"How many HTTP requests with TLS processed on a service, partitioned by TLS version and TLS cipher.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_service_request_duration_seconds","description":"How long it took to process the request on a service, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_service_server_up","description":"service server is up, described by gauge value of 0 or 1.","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"url","value":{"stringValue":"http://127.0.0.1"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
}
|
||||
|
||||
registry.ServiceReqsCounter().With("service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
|
||||
registry.ServiceReqsCounter().With("service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
|
||||
registry.ServiceReqsTLSCounter().With("service", "test", "tls_version", "foo", "tls_cipher", "bar").Add(1)
|
||||
registry.ServiceReqDurationHistogram().With("service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000)
|
||||
registry.ServiceServerUpGauge().With("service", "test", "url", "http://127.0.0.1").Set(1)
|
||||
msgService := <-c
|
||||
|
||||
assertMessage(t, *msgService, expectedService)
|
||||
|
||||
expectedServiceRetries := []string{
|
||||
`({"attributes":\[{"key":"service","value":{"stringValue":"foobar"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1})`,
|
||||
`({"attributes":\[{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":2})`,
|
||||
}
|
||||
|
||||
registry.ServiceRetriesCounter().With("service", "test").Add(1)
|
||||
registry.ServiceRetriesCounter().With("service", "test").Add(1)
|
||||
registry.ServiceRetriesCounter().With("service", "foobar").Add(1)
|
||||
msgServiceRetries := <-c
|
||||
|
||||
assertMessage(t, *msgServiceRetries, expectedServiceRetries)
|
||||
|
||||
expectedServiceOpenConns := []string{
|
||||
`({"attributes":\[{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":3})`,
|
||||
`({"attributes":\[{"key":"service","value":{"stringValue":"foobar"}}\],"startTimeUnixNano":"[\d]{20}","timeUnixNano":"[\d]{19}","asDouble":1})`,
|
||||
}
|
||||
|
||||
registry.ServiceOpenConnsGauge().With("service", "test").Set(1)
|
||||
registry.ServiceOpenConnsGauge().With("service", "test").Add(1)
|
||||
registry.ServiceOpenConnsGauge().With("service", "test").Add(1)
|
||||
registry.ServiceOpenConnsGauge().With("service", "foobar").Add(1)
|
||||
msgServiceOpenConns := <-c
|
||||
|
||||
assertMessage(t, *msgServiceOpenConns, expectedServiceOpenConns)
|
||||
|
||||
expectedEntryPointReqDuration := []string{
|
||||
`({"attributes":\[{"key":"entrypoint","value":{"stringValue":"myEntrypoint"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"2","sum":30000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","2"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":20000})`,
|
||||
}
|
||||
|
||||
registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(10000)
|
||||
registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(20000)
|
||||
msgEntryPointReqDurationHistogram := <-c
|
||||
|
||||
assertMessage(t, *msgEntryPointReqDurationHistogram, expectedEntryPointReqDuration)
|
||||
|
||||
// We need to unlock the HTTP Server for the last export call when stopping
|
||||
// OpenTelemetry.
|
||||
go func() {
|
||||
<-c
|
||||
}()
|
||||
StopOpenTelemetry()
|
||||
}
|
46
pkg/middlewares/contenttype/content_type.go
Normal file
46
pkg/middlewares/contenttype/content_type.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package contenttype
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
)
|
||||
|
||||
const (
|
||||
typeName = "ContentType"
|
||||
)
|
||||
|
||||
// ContentType is a middleware used to activate Content-Type auto-detection.
|
||||
type contentType struct {
|
||||
next http.Handler
|
||||
name string
|
||||
}
|
||||
|
||||
// New creates a new handler.
|
||||
func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) {
|
||||
middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
|
||||
return &contentType{next: next, name: name}, nil
|
||||
}
|
||||
|
||||
func (c *contentType) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// Re-enable auto-detection.
|
||||
if ct, ok := rw.Header()["Content-Type"]; ok && ct == nil {
|
||||
middlewares.GetLogger(req.Context(), c.name, typeName).
|
||||
Debug().Msg("Enable Content-Type auto-detection.")
|
||||
delete(rw.Header(), "Content-Type")
|
||||
}
|
||||
|
||||
c.next.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func DisableAutoDetection(next http.Handler) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
// Prevent Content-Type auto-detection.
|
||||
if _, ok := rw.Header()["Content-Type"]; !ok {
|
||||
rw.Header()["Content-Type"] = nil
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
79
pkg/middlewares/contenttype/content_type_test.go
Normal file
79
pkg/middlewares/contenttype/content_type_test.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package contenttype
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
)
|
||||
|
||||
func TestAutoDetection(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
autoDetect bool
|
||||
contentType string
|
||||
wantContentType string
|
||||
}{
|
||||
{
|
||||
desc: "Keep the Content-Type returned by the server",
|
||||
autoDetect: false,
|
||||
contentType: "application/json",
|
||||
wantContentType: "application/json",
|
||||
},
|
||||
{
|
||||
desc: "Don't auto-detect Content-Type header by default when not set by the server",
|
||||
autoDetect: false,
|
||||
contentType: "",
|
||||
wantContentType: "",
|
||||
},
|
||||
{
|
||||
desc: "Keep the Content-Type returned by the server with auto-detection middleware",
|
||||
autoDetect: true,
|
||||
contentType: "application/json",
|
||||
wantContentType: "application/json",
|
||||
},
|
||||
{
|
||||
desc: "Auto-detect when Content-Type header is not already set by the server with auto-detection middleware",
|
||||
autoDetect: true,
|
||||
contentType: "",
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var next http.Handler
|
||||
next = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if test.contentType != "" {
|
||||
w.Header().Set("Content-Type", test.contentType)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("Test"))
|
||||
})
|
||||
|
||||
if test.autoDetect {
|
||||
var err error
|
||||
next, err = New(context.Background(), next, "foo-content-type")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
server := httptest.NewServer(
|
||||
DisableAutoDetection(next),
|
||||
)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, server.URL, nil)
|
||||
res, err := server.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.wantContentType, res.Header.Get("Content-Type"))
|
||||
})
|
||||
}
|
||||
}
|
240
pkg/muxer/http/matcher.go
Normal file
240
pkg/muxer/http/matcher.go
Normal file
|
@ -0,0 +1,240 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v2/pkg/ip"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var httpFuncs = map[string]func(*mux.Route, ...string) error{
|
||||
"ClientIP": expectNParameters(clientIP, 1),
|
||||
"Method": expectNParameters(method, 1),
|
||||
"Host": expectNParameters(host, 1),
|
||||
"HostRegexp": expectNParameters(hostRegexp, 1),
|
||||
"Path": expectNParameters(path, 1),
|
||||
"PathRegexp": expectNParameters(pathRegexp, 1),
|
||||
"PathPrefix": expectNParameters(pathPrefix, 1),
|
||||
"Header": expectNParameters(header, 2),
|
||||
"HeaderRegexp": expectNParameters(headerRegexp, 2),
|
||||
"Query": expectNParameters(query, 1, 2),
|
||||
"QueryRegexp": expectNParameters(queryRegexp, 1, 2),
|
||||
}
|
||||
|
||||
func expectNParameters(fn func(*mux.Route, ...string) error, n ...int) func(*mux.Route, ...string) error {
|
||||
return func(route *mux.Route, s ...string) error {
|
||||
if !slices.Contains(n, len(s)) {
|
||||
return fmt.Errorf("unexpected number of parameters; got %d, expected one of %v", len(s), n)
|
||||
}
|
||||
|
||||
return fn(route, s...)
|
||||
}
|
||||
}
|
||||
|
||||
func clientIP(route *mux.Route, clientIP ...string) error {
|
||||
checker, err := ip.NewChecker(clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing IP checker for ClientIP matcher: %w", err)
|
||||
}
|
||||
|
||||
strategy := ip.RemoteAddrStrategy{}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
ok, err := checker.Contains(strategy.GetIP(req))
|
||||
if err != nil {
|
||||
log.Ctx(req.Context()).Warn().Err(err).Msg("ClientIP matcher: could not match remote address")
|
||||
return false
|
||||
}
|
||||
|
||||
return ok
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func method(route *mux.Route, methods ...string) error {
|
||||
return route.Methods(methods...).GetError()
|
||||
}
|
||||
|
||||
func host(route *mux.Route, hosts ...string) error {
|
||||
host := hosts[0]
|
||||
|
||||
if !IsASCII(host) {
|
||||
return fmt.Errorf("invalid value %q for Host matcher, non-ASCII characters are not allowed", host)
|
||||
}
|
||||
|
||||
host = strings.ToLower(host)
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
reqHost := requestdecorator.GetCanonizedHost(req.Context())
|
||||
if len(reqHost) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if reqHost == host {
|
||||
return true
|
||||
}
|
||||
|
||||
flatH := requestdecorator.GetCNAMEFlatten(req.Context())
|
||||
if len(flatH) > 0 {
|
||||
return strings.EqualFold(flatH, host)
|
||||
}
|
||||
|
||||
// Check for match on trailing period on host
|
||||
if last := len(host) - 1; last >= 0 && host[last] == '.' {
|
||||
h := host[:last]
|
||||
if reqHost == h {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Check for match on trailing period on request
|
||||
if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' {
|
||||
h := reqHost[:last]
|
||||
if h == host {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hostRegexp(route *mux.Route, hosts ...string) error {
|
||||
host := hosts[0]
|
||||
|
||||
if !IsASCII(host) {
|
||||
return fmt.Errorf("invalid value %q for HostRegexp matcher, non-ASCII characters are not allowed", host)
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compiling HostRegexp matcher: %w", err)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
return re.MatchString(requestdecorator.GetCanonizedHost(req.Context())) ||
|
||||
re.MatchString(requestdecorator.GetCNAMEFlatten(req.Context()))
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func path(route *mux.Route, paths ...string) error {
|
||||
path := paths[0]
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
return req.URL.Path == path
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathRegexp(route *mux.Route, paths ...string) error {
|
||||
path := paths[0]
|
||||
|
||||
re, err := regexp.Compile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compiling PathPrefix matcher: %w", err)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
return re.MatchString(req.URL.Path)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathPrefix(route *mux.Route, paths ...string) error {
|
||||
path := paths[0]
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
return strings.HasPrefix(req.URL.Path, path)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func header(route *mux.Route, headers ...string) error {
|
||||
return route.Headers(headers...).GetError()
|
||||
}
|
||||
|
||||
func headerRegexp(route *mux.Route, headers ...string) error {
|
||||
return route.HeadersRegexp(headers...).GetError()
|
||||
}
|
||||
|
||||
func query(route *mux.Route, queries ...string) error {
|
||||
key := queries[0]
|
||||
|
||||
var value string
|
||||
if len(queries) == 2 {
|
||||
value = queries[1]
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
values, ok := req.URL.Query()[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return slices.Contains(values, value)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryRegexp(route *mux.Route, queries ...string) error {
|
||||
if len(queries) == 1 {
|
||||
return query(route, queries...)
|
||||
}
|
||||
|
||||
key, value := queries[0], queries[1]
|
||||
|
||||
re, err := regexp.Compile(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compiling QueryRegexp matcher: %w", err)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
values, ok := req.URL.Query()[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
idx := slices.IndexFunc(values, func(value string) bool {
|
||||
return re.MatchString(value)
|
||||
})
|
||||
|
||||
return idx >= 0
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsASCII checks if the given string contains only ASCII characters.
|
||||
func IsASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
999
pkg/muxer/http/matcher_test.go
Normal file
999
pkg/muxer/http/matcher_test.go
Normal file
|
@ -0,0 +1,999 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||
)
|
||||
|
||||
func TestClientIPMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid ClientIP matcher",
|
||||
rule: "ClientIP(`1`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid ClientIP matcher (no parameter)",
|
||||
rule: "ClientIP()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid ClientIP matcher (empty parameter)",
|
||||
rule: "ClientIP(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid ClientIP matcher (too many parameters)",
|
||||
rule: "ClientIP(`127.0.0.1`, `192.168.1.0/24`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid ClientIP matcher",
|
||||
rule: "ClientIP(`127.0.0.1`)",
|
||||
expected: map[string]int{
|
||||
"127.0.0.1": http.StatusOK,
|
||||
"192.168.1.1": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid ClientIP matcher but invalid remote address",
|
||||
rule: "ClientIP(`127.0.0.1`)",
|
||||
expected: map[string]int{
|
||||
"1": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid ClientIP matcher using CIDR",
|
||||
rule: "ClientIP(`192.168.1.0/24`)",
|
||||
expected: map[string]int{
|
||||
"192.168.1.1": http.StatusOK,
|
||||
"192.168.1.100": http.StatusOK,
|
||||
"192.168.2.1": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for remoteAddr := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://example.com", http.NoBody)
|
||||
req.RemoteAddr = remoteAddr
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[remoteAddr] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMethodMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid Method matcher (no parameter)",
|
||||
rule: "Method()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Method matcher (empty parameter)",
|
||||
rule: "Method(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Method matcher (too many parameters)",
|
||||
rule: "Method(`GET`, `POST`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid Method matcher",
|
||||
rule: "Method(`GET`)",
|
||||
expected: map[string]int{
|
||||
http.MethodGet: http.StatusOK,
|
||||
http.MethodPost: http.StatusMethodNotAllowed,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Method matcher (lower case)",
|
||||
rule: "Method(`get`)",
|
||||
expected: map[string]int{
|
||||
http.MethodGet: http.StatusOK,
|
||||
http.MethodPost: http.StatusMethodNotAllowed,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for method := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(method, "https://example.com", http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[method] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid Host matcher (no parameter)",
|
||||
rule: "Host()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Host matcher (empty parameter)",
|
||||
rule: "Host(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Host matcher (non-ASCII)",
|
||||
rule: "Host(`🦭.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Host matcher (too many parameters)",
|
||||
rule: "Host(`example.com`, `example.org`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid Host matcher",
|
||||
rule: "Host(`example.com`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusOK,
|
||||
"https://example.com:8080": http.StatusOK,
|
||||
"https://example.com/path": http.StatusOK,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Host matcher - matcher ending with a dot",
|
||||
rule: "Host(`example.com.`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusOK,
|
||||
"https://example.com/path": http.StatusOK,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
"https://example.com.": http.StatusOK,
|
||||
"https://example.com./path": http.StatusOK,
|
||||
"https://example.org.": http.StatusNotFound,
|
||||
"https://example.org./path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Host matcher - URL ending with a dot",
|
||||
rule: "Host(`example.com`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com.": http.StatusOK,
|
||||
"https://example.com./path": http.StatusOK,
|
||||
"https://example.org.": http.StatusNotFound,
|
||||
"https://example.org./path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Host matcher - matcher with UPPER case",
|
||||
rule: "Host(`EXAMPLE.COM`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusOK,
|
||||
"https://example.com/path": http.StatusOK,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Host matcher - puny-coded emoji",
|
||||
rule: "Host(`xn--9t9h.com`)",
|
||||
expected: map[string]int{
|
||||
"https://xn--9t9h.com": http.StatusOK,
|
||||
"https://xn--9t9h.com/path": http.StatusOK,
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/path": http.StatusNotFound,
|
||||
// The request's sender must use puny-code.
|
||||
"https://🦭.com": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// RequestDecorator is necessary for the Host matcher
|
||||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
reqHost.ServeHTTP(w, req, muxer.ServeHTTP)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostRegexpMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid HostRegexp matcher (no parameter)",
|
||||
rule: "HostRegexp()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HostRegexp matcher (empty parameter)",
|
||||
rule: "HostRegexp(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HostRegexp matcher (non-ASCII)",
|
||||
rule: "HostRegexp(`🦭.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HostRegexp matcher (invalid regexp)",
|
||||
rule: "HostRegexp(`(example.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HostRegexp matcher (too many parameters)",
|
||||
rule: "HostRegexp(`example.com`, `example.org`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid HostRegexp matcher",
|
||||
rule: "HostRegexp(`^[a-zA-Z-]+\\.com$`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusOK,
|
||||
"https://example.com:8080": http.StatusOK,
|
||||
"https://example.com/path": http.StatusOK,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid HostRegexp matcher with case sensitive regexp",
|
||||
rule: "HostRegexp(`^[A-Z]+\\.com$`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://EXAMPLE.com": http.StatusNotFound,
|
||||
"https://example.com/path": http.StatusNotFound,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid HostRegexp matcher with Traefik v2 syntax",
|
||||
rule: "HostRegexp(`{domain:[a-zA-Z-]+\\.com}`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/path": http.StatusNotFound,
|
||||
"https://example.org": http.StatusNotFound,
|
||||
"https://example.org/path": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// RequestDecorator is necessary for the HostRegexp matcher
|
||||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
reqHost.ServeHTTP(w, req, muxer.ServeHTTP)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid Path matcher (no parameter)",
|
||||
rule: "Path()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Path matcher (empty parameter)",
|
||||
rule: "Path(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Path matcher (no leading /)",
|
||||
rule: "Path(`css`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Path matcher (too many parameters)",
|
||||
rule: "Path(`/css`, `/js`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid Path matcher",
|
||||
rule: "Path(`/css`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/html": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/css/": http.StatusNotFound,
|
||||
"https://example.com/css/main.css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathRegexpMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid PathRegexp matcher (no parameter)",
|
||||
rule: "PathRegexp()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathRegexp matcher (empty parameter)",
|
||||
rule: "PathRegexp(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathRegexp matcher (invalid regexp)",
|
||||
rule: "PathRegexp(`/(css`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathRegexp matcher (too many parameters)",
|
||||
rule: "PathRegexp(`/css`, `/js`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid PathRegexp matcher",
|
||||
rule: "PathRegexp(`^/(css|js)`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/html": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.com/CSS": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/css/": http.StatusOK,
|
||||
"https://example.com/css/main.css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.com/js/": http.StatusOK,
|
||||
"https://example.com/js/main.js": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid PathRegexp matcher with Traefik v2 syntax",
|
||||
rule: `PathRegexp("/{path:(css|js)}")`,
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/html": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusNotFound,
|
||||
"https://example.com/{path:css}": http.StatusOK,
|
||||
"https://example.com/{path:css}/": http.StatusOK,
|
||||
"https://example.com/%7Bpath:css%7D": http.StatusOK,
|
||||
"https://example.com/%7Bpath:css%7D/": http.StatusOK,
|
||||
"https://example.com/{path:js}": http.StatusOK,
|
||||
"https://example.com/{path:js}/": http.StatusOK,
|
||||
"https://example.com/%7Bpath:js%7D": http.StatusOK,
|
||||
"https://example.com/%7Bpath:js%7D/": http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathPrefixMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid PathPrefix matcher (no parameter)",
|
||||
rule: "PathPrefix()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathPrefix matcher (empty parameter)",
|
||||
rule: "PathPrefix(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathPrefix matcher (no leading /)",
|
||||
rule: "PathPrefix(`css`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid PathPrefix matcher (too many parameters)",
|
||||
rule: "PathPrefix(`/css`, `/js`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid PathPrefix matcher",
|
||||
rule: `PathPrefix("/css")`,
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/html": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/css/": http.StatusOK,
|
||||
"https://example.com/css/main.css": http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[*http.Header]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid Header matcher (no parameter)",
|
||||
rule: "Header()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Header matcher (missing value parameter)",
|
||||
rule: "Header(`X-Forwarded-Host`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Header matcher (missing value parameter)",
|
||||
rule: "Header(`X-Forwarded-Host`, ``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Header matcher (missing key parameter)",
|
||||
rule: "Header(``, `example.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Header matcher (too many parameters)",
|
||||
rule: "Header(`X-Forwarded-Host`, `example.com`, `example.org`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid Header matcher",
|
||||
rule: "Header(`X-Forwarded-Proto`, `https`)",
|
||||
expected: map[*http.Header]int{
|
||||
{"X-Forwarded-Proto": []string{"https"}}: http.StatusOK,
|
||||
{"x-forwarded-proto": []string{"https"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"http", "https"}}: http.StatusOK,
|
||||
{"X-Forwarded-Proto": []string{"https", "http"}}: http.StatusOK,
|
||||
{"X-Forwarded-Host": []string{"example.com"}}: http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
for headers := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://example.com", http.NoBody)
|
||||
req.Header = *headers
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
assert.Equal(t, test.expected[headers], w.Code, headers)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderRegexpMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[*http.Header]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (no parameter)",
|
||||
rule: "HeaderRegexp()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (missing value parameter)",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Host`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (missing value parameter)",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Host`, ``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (missing key parameter)",
|
||||
rule: "HeaderRegexp(``, `example.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (invalid regexp)",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Host`,`(example.com`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid HeaderRegexp matcher (too many parameters)",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Host`, `example.com`, `example.org`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid HeaderRegexp matcher",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Proto`, `^https?$`)",
|
||||
expected: map[*http.Header]int{
|
||||
{"X-Forwarded-Proto": []string{"http"}}: http.StatusOK,
|
||||
{"x-forwarded-proto": []string{"http"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"https"}}: http.StatusOK,
|
||||
{"X-Forwarded-Proto": []string{"HTTPS"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"ws", "https"}}: http.StatusOK,
|
||||
{"X-Forwarded-Host": []string{"example.com"}}: http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid HeaderRegexp matcher with Traefik v2 syntax",
|
||||
rule: "HeaderRegexp(`X-Forwarded-Proto`, `http{secure:s?}`)",
|
||||
expected: map[*http.Header]int{
|
||||
{"X-Forwarded-Proto": []string{"http"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"https"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"http{secure:}"}}: http.StatusOK,
|
||||
{"X-Forwarded-Proto": []string{"HTTP{secure:}"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"http{secure:s}"}}: http.StatusOK,
|
||||
{"X-Forwarded-Proto": []string{"http{secure:S}"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"HTTPS"}}: http.StatusNotFound,
|
||||
{"X-Forwarded-Proto": []string{"ws", "http{secure:s}"}}: http.StatusOK,
|
||||
{"X-Forwarded-Host": []string{"example.com"}}: http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
for headers := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://example.com", http.NoBody)
|
||||
req.Header = *headers
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
assert.Equal(t, test.expected[headers], w.Code, *headers)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid Query matcher (no parameter)",
|
||||
rule: "Query()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Query matcher (empty key, one parameter)",
|
||||
rule: "Query(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Query matcher (empty key)",
|
||||
rule: "Query(``, `traefik`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Query matcher (empty value)",
|
||||
rule: "Query(`q`, ``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid Query matcher (too many parameters)",
|
||||
rule: "Query(`q`, `traefik`, `proxy`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid Query matcher",
|
||||
rule: "Query(`q`, `traefik`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com?q=traefik": http.StatusOK,
|
||||
"https://example.com?rel=ddg&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=traefik&q=proxy": http.StatusOK,
|
||||
"https://example.com?q=awesome&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=nginx": http.StatusNotFound,
|
||||
"https://example.com?rel=ddg": http.StatusNotFound,
|
||||
"https://example.com?q=TRAEFIK": http.StatusNotFound,
|
||||
"https://example.com?Q=traefik": http.StatusNotFound,
|
||||
"https://example.com?rel=traefik": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Query matcher with empty value",
|
||||
rule: "Query(`mobile`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com?mobile": http.StatusOK,
|
||||
"https://example.com?mobile=true": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryRegexpMatcher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "invalid QueryRegexp matcher (no parameter)",
|
||||
rule: "QueryRegexp()",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid QueryRegexp matcher (empty parameter)",
|
||||
rule: "QueryRegexp(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid QueryRegexp matcher (invalid regexp)",
|
||||
rule: "QueryRegexp(`q`, `(traefik`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid QueryRegexp matcher (too many parameters)",
|
||||
rule: "QueryRegexp(`q`, `traefik`, `proxy`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "valid QueryRegexp matcher",
|
||||
rule: "QueryRegexp(`q`, `^(traefik|nginx)$`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com?q=traefik": http.StatusOK,
|
||||
"https://example.com?rel=ddg&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=traefik&q=proxy": http.StatusOK,
|
||||
"https://example.com?q=awesome&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=TRAEFIK": http.StatusNotFound,
|
||||
"https://example.com?Q=traefik": http.StatusNotFound,
|
||||
"https://example.com?rel=traefik": http.StatusNotFound,
|
||||
"https://example.com?q=nginx": http.StatusOK,
|
||||
"https://example.com?rel=ddg&q=nginx": http.StatusOK,
|
||||
"https://example.com?q=nginx&q=proxy": http.StatusOK,
|
||||
"https://example.com?q=awesome&q=nginx": http.StatusOK,
|
||||
"https://example.com?q=NGINX": http.StatusNotFound,
|
||||
"https://example.com?Q=nginx": http.StatusNotFound,
|
||||
"https://example.com?rel=nginx": http.StatusNotFound,
|
||||
"https://example.com?q=haproxy": http.StatusNotFound,
|
||||
"https://example.com?rel=ddg": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid QueryRegexp matcher",
|
||||
rule: "QueryRegexp(`q`, `^.*$`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com?q=traefik": http.StatusOK,
|
||||
"https://example.com?rel=ddg&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=traefik&q=proxy": http.StatusOK,
|
||||
"https://example.com?q=awesome&q=traefik": http.StatusOK,
|
||||
"https://example.com?q=TRAEFIK": http.StatusOK,
|
||||
"https://example.com?Q=traefik": http.StatusNotFound,
|
||||
"https://example.com?rel=traefik": http.StatusNotFound,
|
||||
"https://example.com?q=nginx": http.StatusOK,
|
||||
"https://example.com?rel=ddg&q=nginx": http.StatusOK,
|
||||
"https://example.com?q=nginx&q=proxy": http.StatusOK,
|
||||
"https://example.com?q=awesome&q=nginx": http.StatusOK,
|
||||
"https://example.com?q=NGINX": http.StatusOK,
|
||||
"https://example.com?Q=nginx": http.StatusNotFound,
|
||||
"https://example.com?rel=nginx": http.StatusNotFound,
|
||||
"https://example.com?q=haproxy": http.StatusOK,
|
||||
"https://example.com?rel=ddg": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid QueryRegexp matcher with Traefik v2 syntax",
|
||||
rule: "QueryRegexp(`q`, `{value:(traefik|nginx)}`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com?q=traefik": http.StatusNotFound,
|
||||
"https://example.com?q={value:traefik}": http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,32 +3,12 @@ package http
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v2/pkg/ip"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||
"github.com/traefik/traefik/v2/pkg/rules"
|
||||
"github.com/vulcand/predicate"
|
||||
)
|
||||
|
||||
const hostMatcher = "Host"
|
||||
|
||||
var httpFuncs = map[string]func(*mux.Route, ...string) error{
|
||||
hostMatcher: host,
|
||||
"HostHeader": host,
|
||||
"HostRegexp": hostRegexp,
|
||||
"ClientIP": clientIP,
|
||||
"Path": path,
|
||||
"PathPrefix": pathPrefix,
|
||||
"Method": methods,
|
||||
"Headers": headers,
|
||||
"HeadersRegexp": headersRegexp,
|
||||
"Query": query,
|
||||
}
|
||||
|
||||
// Muxer handles routing with rules.
|
||||
type Muxer struct {
|
||||
*mux.Router
|
||||
|
@ -80,171 +60,6 @@ func (r *Muxer) AddRoute(rule string, priority int, handler http.Handler) error
|
|||
return nil
|
||||
}
|
||||
|
||||
// ParseDomains extract domains from rule.
|
||||
func ParseDomains(rule string) ([]string, error) {
|
||||
var matchers []string
|
||||
for matcher := range httpFuncs {
|
||||
matchers = append(matchers, matcher)
|
||||
}
|
||||
|
||||
parser, err := rules.NewParser(matchers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parse, err := parser.Parse(rule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buildTree, ok := parse.(rules.TreeBuilder)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error while parsing rule %s", rule)
|
||||
}
|
||||
|
||||
return buildTree().ParseMatchers([]string{hostMatcher}), nil
|
||||
}
|
||||
|
||||
func path(route *mux.Route, paths ...string) error {
|
||||
rt := route.Subrouter()
|
||||
|
||||
for _, path := range paths {
|
||||
if err := rt.Path(path).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathPrefix(route *mux.Route, paths ...string) error {
|
||||
rt := route.Subrouter()
|
||||
|
||||
for _, path := range paths {
|
||||
if err := rt.PathPrefix(path).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func host(route *mux.Route, hosts ...string) error {
|
||||
for i, host := range hosts {
|
||||
if !IsASCII(host) {
|
||||
return fmt.Errorf("invalid value %q for \"Host\" matcher, non-ASCII characters are not allowed", host)
|
||||
}
|
||||
|
||||
hosts[i] = strings.ToLower(host)
|
||||
}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
reqHost := requestdecorator.GetCanonizedHost(req.Context())
|
||||
if len(reqHost) == 0 {
|
||||
// If the request is an HTTP/1.0 request, then a Host may not be defined.
|
||||
if req.ProtoAtLeast(1, 1) {
|
||||
log.Ctx(req.Context()).Warn().Msgf("Could not retrieve CanonizedHost, rejecting %s", req.Host)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
flatH := requestdecorator.GetCNAMEFlatten(req.Context())
|
||||
if len(flatH) > 0 {
|
||||
for _, host := range hosts {
|
||||
if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) {
|
||||
return true
|
||||
}
|
||||
log.Ctx(req.Context()).Debug().Msgf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
if reqHost == host {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for match on trailing period on host
|
||||
if last := len(host) - 1; last >= 0 && host[last] == '.' {
|
||||
h := host[:last]
|
||||
if reqHost == h {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Check for match on trailing period on request
|
||||
if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' {
|
||||
h := reqHost[:last]
|
||||
if h == host {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func clientIP(route *mux.Route, clientIPs ...string) error {
|
||||
checker, err := ip.NewChecker(clientIPs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
|
||||
}
|
||||
|
||||
strategy := ip.RemoteAddrStrategy{}
|
||||
|
||||
route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
|
||||
ok, err := checker.Contains(strategy.GetIP(req))
|
||||
if err != nil {
|
||||
log.Ctx(req.Context()).Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
|
||||
return false
|
||||
}
|
||||
|
||||
return ok
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hostRegexp(route *mux.Route, hosts ...string) error {
|
||||
router := route.Subrouter()
|
||||
for _, host := range hosts {
|
||||
if !IsASCII(host) {
|
||||
return fmt.Errorf("invalid value %q for HostRegexp matcher, non-ASCII characters are not allowed", host)
|
||||
}
|
||||
|
||||
tmpRt := router.Host(host)
|
||||
if tmpRt.GetError() != nil {
|
||||
return tmpRt.GetError()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func methods(route *mux.Route, methods ...string) error {
|
||||
return route.Methods(methods...).GetError()
|
||||
}
|
||||
|
||||
func headers(route *mux.Route, headers ...string) error {
|
||||
return route.Headers(headers...).GetError()
|
||||
}
|
||||
|
||||
func headersRegexp(route *mux.Route, headers ...string) error {
|
||||
return route.HeadersRegexp(headers...).GetError()
|
||||
}
|
||||
|
||||
func query(route *mux.Route, query ...string) error {
|
||||
var queries []string
|
||||
for _, elem := range query {
|
||||
queries = append(queries, strings.SplitN(elem, "=", 2)...)
|
||||
}
|
||||
|
||||
route.Queries(queries...)
|
||||
// Queries can return nil so we can't chain the GetError()
|
||||
return route.GetError()
|
||||
}
|
||||
|
||||
func addRuleOnRouter(router *mux.Router, rule *rules.Tree) error {
|
||||
switch rule.Matcher {
|
||||
case "and":
|
||||
|
@ -276,20 +91,6 @@ func addRuleOnRouter(router *mux.Router, rule *rules.Tree) error {
|
|||
}
|
||||
}
|
||||
|
||||
func not(m func(*mux.Route, ...string) error) func(*mux.Route, ...string) error {
|
||||
return func(r *mux.Route, v ...string) error {
|
||||
router := mux.NewRouter()
|
||||
err := m(router.NewRoute(), v...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.MatcherFunc(func(req *http.Request, ma *mux.RouteMatch) bool {
|
||||
return !router.Match(req, ma)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func addRuleOnRoute(route *mux.Route, rule *rules.Tree) error {
|
||||
switch rule.Matcher {
|
||||
case "and":
|
||||
|
@ -322,13 +123,44 @@ func addRuleOnRoute(route *mux.Route, rule *rules.Tree) error {
|
|||
}
|
||||
}
|
||||
|
||||
// IsASCII checks if the given string contains only ASCII characters.
|
||||
func IsASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
func not(m func(*mux.Route, ...string) error) func(*mux.Route, ...string) error {
|
||||
return func(r *mux.Route, v ...string) error {
|
||||
router := mux.NewRouter()
|
||||
|
||||
err := m(router.NewRoute(), v...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.MatcherFunc(func(req *http.Request, ma *mux.RouteMatch) bool {
|
||||
return !router.Match(req, ma)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ParseDomains extract domains from rule.
|
||||
func ParseDomains(rule string) ([]string, error) {
|
||||
var matchers []string
|
||||
for matcher := range httpFuncs {
|
||||
matchers = append(matchers, matcher)
|
||||
}
|
||||
|
||||
return true
|
||||
parser, err := rules.NewParser(matchers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parse, err := parser.Parse(rule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buildTree, ok := parse.(rules.TreeBuilder)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error while parsing rule %s", rule)
|
||||
}
|
||||
|
||||
return buildTree().ParseMatchers([]string{"Host"}), nil
|
||||
}
|
||||
|
|
|
@ -7,14 +7,13 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
)
|
||||
|
||||
func Test_addRoute(t *testing.T) {
|
||||
func TestMuxer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
|
@ -33,607 +32,177 @@ func Test_addRoute(t *testing.T) {
|
|||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Host empty",
|
||||
rule: "Host(``)",
|
||||
desc: "Rule without quote",
|
||||
rule: "Host(example.com)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "PathPrefix empty",
|
||||
rule: "PathPrefix(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "PathPrefix",
|
||||
rule: "PathPrefix(`/foo`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "wrong PathPrefix",
|
||||
rule: "PathPrefix(`/bar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host",
|
||||
rule: "Host(`localhost`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Non-ASCII Host",
|
||||
rule: "Host(`locàlhost`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Non-ASCII HostRegexp",
|
||||
rule: "HostRegexp(`locàlhost`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "HostHeader equivalent to Host",
|
||||
rule: "HostHeader(`localhost`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
"http://bar/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host with trailing period in rule",
|
||||
rule: "Host(`localhost.`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host with trailing period in domain",
|
||||
rule: "Host(`localhost`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost./foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host with trailing period in domain and rule",
|
||||
rule: "Host(`localhost.`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost./foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "wrong Host",
|
||||
rule: "Host(`nope`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix",
|
||||
rule: "Host(`localhost`) && PathPrefix(`/foo`)",
|
||||
rule: "Host(`localhost`) && PathPrefix(`/css`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix wrong PathPrefix",
|
||||
rule: "Host(`localhost`) && PathPrefix(`/bar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix wrong Host",
|
||||
rule: "Host(`nope`) && PathPrefix(`/foo`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix Host OR, first host",
|
||||
rule: "Host(`nope`,`localhost`) && PathPrefix(`/foo`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix Host OR, second host",
|
||||
rule: "Host(`nope`,`localhost`) && PathPrefix(`/foo`)",
|
||||
expected: map[string]int{
|
||||
"http://nope/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Host and PathPrefix Host OR, first host and wrong PathPrefix",
|
||||
rule: "Host(`nope,localhost`) && PathPrefix(`/bar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "HostRegexp with capturing group",
|
||||
rule: "HostRegexp(`{subdomain:(foo\\.)?bar\\.com}`)",
|
||||
expected: map[string]int{
|
||||
"http://foo.bar.com": http.StatusOK,
|
||||
"http://bar.com": http.StatusOK,
|
||||
"http://fooubar.com": http.StatusNotFound,
|
||||
"http://barucom": http.StatusNotFound,
|
||||
"http://barcom": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "HostRegexp with non capturing group",
|
||||
rule: "HostRegexp(`{subdomain:(?:foo\\.)?bar\\.com}`)",
|
||||
expected: map[string]int{
|
||||
"http://foo.bar.com": http.StatusOK,
|
||||
"http://bar.com": http.StatusOK,
|
||||
"http://fooubar.com": http.StatusNotFound,
|
||||
"http://barucom": http.StatusNotFound,
|
||||
"http://barcom": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Methods with GET",
|
||||
rule: "Method(`GET`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Methods with GET and POST",
|
||||
rule: "Method(`GET`,`POST`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Methods with POST",
|
||||
rule: "Method(`POST`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusMethodNotAllowed,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Header with matching header",
|
||||
rule: "Headers(`Content-Type`,`application/json`)",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Header without matching header",
|
||||
rule: "Headers(`Content-Type`,`application/foo`)",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "HeaderRegExp with matching header",
|
||||
rule: "HeadersRegexp(`Content-Type`, `application/(text|json)`)",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "HeaderRegExp without matching header",
|
||||
rule: "HeadersRegexp(`Content-Type`, `application/(text|json)`)",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/foo",
|
||||
},
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "HeaderRegExp with matching second header",
|
||||
rule: "HeadersRegexp(`Content-Type`, `application/(text|json)`)",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/text",
|
||||
},
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Query with multiple params",
|
||||
rule: "Query(`foo=bar`, `bar=baz`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo?foo=bar&bar=baz": http.StatusOK,
|
||||
"http://localhost/foo?bar=baz": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Query with multiple equals",
|
||||
rule: "Query(`foo=b=ar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo?foo=b=ar": http.StatusOK,
|
||||
"http://localhost/foo?foo=bar": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with simple path",
|
||||
rule: `Path("/a")`,
|
||||
expected: map[string]int{
|
||||
"http://plop/a": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: `Rule with a simple host`,
|
||||
rule: `Host("plop")`,
|
||||
expected: map[string]int{
|
||||
"http://plop": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with Path AND Host",
|
||||
rule: `Path("/a") && Host("plop")`,
|
||||
expected: map[string]int{
|
||||
"http://plop/a": http.StatusOK,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://localhost/css": http.StatusOK,
|
||||
"https://localhost/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with Host OR Host",
|
||||
rule: `Host("tchouk") || Host("pouet")`,
|
||||
rule: "Host(`example.com`) || Host(`example.org`)",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://pouet/a": http.StatusOK,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusOK,
|
||||
"https://example.eu/html": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with host OR (host AND path)",
|
||||
rule: `Host("tchouk") || (Host("pouet") && Path("/powpow"))`,
|
||||
rule: `Host("example.com") || (Host("example.org") && Path("/css"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
"https://example.eu/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with host OR host AND path",
|
||||
rule: `Host("tchouk") || Host("pouet") && Path("/powpow")`,
|
||||
rule: `Host("example.com") || Host("example.org") && Path("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
"https://example.eu/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with (host OR host) AND path",
|
||||
rule: `(Host("tchouk") || Host("pouet")) && Path("/powpow")`,
|
||||
rule: `(Host("example.com") || Host("example.org")) && Path("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with multiple host AND path",
|
||||
rule: `(Host("tchouk","pouet")) && Path("/powpow")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with multiple host AND multiple path",
|
||||
rule: `Host("tchouk","pouet") && Path("/powpow", "/titi")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://pouet/titi": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
"https://example.eu/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with (host AND path) OR (host AND path)",
|
||||
rule: `(Host("tchouk") && Path("/titi")) || ((Host("pouet")) && Path("/powpow"))`,
|
||||
rule: `(Host("example.com") && Path("/js")) || ((Host("example.org")) && Path("/css"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"http://pouet/powpow": http.StatusOK,
|
||||
"http://pouet/toto": http.StatusNotFound,
|
||||
"http://plopi/a": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusNotFound,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
"https://example.eu/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule without quote",
|
||||
rule: `Host(tchouk)`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule case UPPER",
|
||||
rule: `(HOST("tchouk") && PATHPREFIX("/titi"))`,
|
||||
rule: `PATHPREFIX("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule case lower",
|
||||
rule: `(host("tchouk") && pathprefix("/titi"))`,
|
||||
rule: `pathprefix("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule case CamelCase",
|
||||
rule: `(Host("tchouk") && PathPrefix("/titi"))`,
|
||||
rule: `PathPrefix("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule case Title",
|
||||
rule: `(Host("tchouk") && Pathprefix("/titi"))`,
|
||||
rule: `Pathprefix("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule Path with error",
|
||||
rule: `Path("titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule PathPrefix with error",
|
||||
rule: `PathPrefix("titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule HostRegexp with error",
|
||||
rule: `HostRegexp("{test")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule Headers with error",
|
||||
rule: `Headers("titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule HeadersRegexp with error",
|
||||
rule: `HeadersRegexp("titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule Query",
|
||||
rule: `Query("titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule Query with bad syntax",
|
||||
rule: `Query("titi={test")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule with Path without args",
|
||||
rule: `Host("tchouk") && Path()`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule with an empty path",
|
||||
rule: `Host("tchouk") && Path("")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule with an empty path",
|
||||
rule: `Host("tchouk") && Path("", "/titi")`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Rule with not",
|
||||
rule: `!Host("tchouk")`,
|
||||
rule: `!Host("example.com")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://test/powpow": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on Path",
|
||||
rule: `!Path("/titi")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://tchouk/powpow": http.StatusOK,
|
||||
"https://example.org": http.StatusOK,
|
||||
"https://example.com": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on multiple route with or",
|
||||
rule: `!(Host("tchouk") || Host("toto"))`,
|
||||
rule: `!(Host("example.com") || Host("example.org"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://toto/powpow": http.StatusNotFound,
|
||||
"http://test/powpow": http.StatusOK,
|
||||
"https://example.eu/js": http.StatusOK,
|
||||
"https://example.com/css": http.StatusNotFound,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on multiple route with and",
|
||||
rule: `!(Host("tchouk") && Path("/titi"))`,
|
||||
rule: `!(Host("example.com") && Path("/css"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://test/titi": http.StatusOK,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.eu/css": http.StatusOK,
|
||||
"https://example.com/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on multiple route with and another not",
|
||||
rule: `!(Host("tchouk") && !Path("/titi"))`,
|
||||
rule: `!(Host("example.com") && !Path("/css"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://toto/titi": http.StatusOK,
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on two rule",
|
||||
rule: `!Host("tchouk") || !Path("/titi")`,
|
||||
rule: `!Host("example.com") || !Path("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://test/titi": http.StatusOK,
|
||||
"https://example.com/js": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.com/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule case with double not",
|
||||
rule: `!(!(Host("tchouk") && Pathprefix("/titi")))`,
|
||||
rule: `!(!(Host("example.com") && Pathprefix("/css")))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"http://test/titi": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
"https://example.org/css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule case with not domain",
|
||||
rule: `!Host("tchouk") && Pathprefix("/titi")`,
|
||||
rule: `!Host("example.com") && Pathprefix("/css")`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"http://toto/powpow": http.StatusNotFound,
|
||||
"http://toto/titi": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/js": http.StatusNotFound,
|
||||
"https://example.com/css": http.StatusNotFound,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with multiple host AND multiple path AND not",
|
||||
rule: `!(Host("tchouk","pouet") && Path("/powpow", "/titi"))`,
|
||||
rule: `!(Host("example.com") && Path("/js"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"http://tchouk/powpow": http.StatusNotFound,
|
||||
"http://pouet/powpow": http.StatusNotFound,
|
||||
"http://tchouk/titi": http.StatusNotFound,
|
||||
"http://pouet/titi": http.StatusNotFound,
|
||||
"http://pouet/toto": http.StatusOK,
|
||||
"http://plopi/a": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "ClientIP empty",
|
||||
rule: "ClientIP(``)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ClientIP",
|
||||
rule: "ClientIP(`invalid`)",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "Non matching ClientIP",
|
||||
rule: "ClientIP(`10.10.1.1`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Non matching IPv6",
|
||||
rule: "ClientIP(`10::10`)",
|
||||
remoteAddr: "::1",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP",
|
||||
rule: "ClientIP(`10.0.0.0`)",
|
||||
remoteAddr: "10.0.0.0:8456",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IPv6",
|
||||
rule: "ClientIP(`10::10`)",
|
||||
remoteAddr: "10::10",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP among several IP",
|
||||
rule: "ClientIP(`10.0.0.1`, `10.0.0.0`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Non Matching IP with CIDR",
|
||||
rule: "ClientIP(`11.0.0.0/24`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Non Matching IPv6 with CIDR",
|
||||
rule: "ClientIP(`11::/16`)",
|
||||
remoteAddr: "10::",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP with CIDR",
|
||||
rule: "ClientIP(`10.0.0.0/16`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IPv6 with CIDR",
|
||||
rule: "ClientIP(`10::/16`)",
|
||||
remoteAddr: "10::10",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP among several CIDR",
|
||||
rule: "ClientIP(`11.0.0.0/16`, `10.0.0.0/16`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP among non matching CIDR and matching IP",
|
||||
rule: "ClientIP(`11.0.0.0/16`, `10.0.0.0`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching IP among matching CIDR and non matching IP",
|
||||
rule: "ClientIP(`11.0.0.0`, `10.0.0.0/16`)",
|
||||
remoteAddr: "10.0.0.0",
|
||||
expected: map[string]int{
|
||||
"http://tchouk/toto": http.StatusOK,
|
||||
"https://example.com/js": http.StatusNotFound,
|
||||
"https://example.com/html": http.StatusOK,
|
||||
"https://example.org/js": http.StatusOK,
|
||||
"https://example.com/css": http.StatusOK,
|
||||
"https://example.org/css": http.StatusOK,
|
||||
"https://example.org/html": http.StatusOK,
|
||||
"https://example.eu/images": http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -644,36 +213,37 @@ func Test_addRoute(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
err = muxer.AddRoute(test.rule, 0, handler)
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
// RequestDecorator is necessary for the host rule
|
||||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, calledURL, nil)
|
||||
|
||||
// Useful for the ClientIP matcher
|
||||
req.RemoteAddr = test.remoteAddr
|
||||
|
||||
for key, value := range test.headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
reqHost.ServeHTTP(w, req, muxer.ServeHTTP)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
assert.Equal(t, test.expected, results)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// RequestDecorator is necessary for the host rule
|
||||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
results := make(map[string]int)
|
||||
for calledURL := range test.expected {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, calledURL, http.NoBody)
|
||||
|
||||
// Useful for the ClientIP matcher
|
||||
req.RemoteAddr = test.remoteAddr
|
||||
|
||||
for key, value := range test.headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
reqHost.ServeHTTP(w, req, muxer.ServeHTTP)
|
||||
results[calledURL] = w.Code
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expected, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -813,7 +383,7 @@ func Test_addRoutePriority(t *testing.T) {
|
|||
muxer.SortRoutes()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, test.path, nil)
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, test.path, http.NoBody)
|
||||
|
||||
muxer.ServeHTTP(w, req)
|
||||
|
||||
|
@ -822,86 +392,6 @@ func Test_addRoutePriority(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHostRegexp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
hostExp string
|
||||
urls map[string]bool
|
||||
}{
|
||||
{
|
||||
desc: "capturing group",
|
||||
hostExp: "{subdomain:(foo\\.)?bar\\.com}",
|
||||
urls: map[string]bool{
|
||||
"http://foo.bar.com": true,
|
||||
"http://bar.com": true,
|
||||
"http://fooubar.com": false,
|
||||
"http://barucom": false,
|
||||
"http://barcom": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "non capturing group",
|
||||
hostExp: "{subdomain:(?:foo\\.)?bar\\.com}",
|
||||
urls: map[string]bool{
|
||||
"http://foo.bar.com": true,
|
||||
"http://bar.com": true,
|
||||
"http://fooubar.com": false,
|
||||
"http://barucom": false,
|
||||
"http://barcom": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "regex insensitive",
|
||||
hostExp: "{dummy:[A-Za-z-]+\\.bar\\.com}",
|
||||
urls: map[string]bool{
|
||||
"http://FOO.bar.com": true,
|
||||
"http://foo.bar.com": true,
|
||||
"http://fooubar.com": false,
|
||||
"http://barucom": false,
|
||||
"http://barcom": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "insensitive host",
|
||||
hostExp: "{dummy:[a-z-]+\\.bar\\.com}",
|
||||
urls: map[string]bool{
|
||||
"http://FOO.bar.com": true,
|
||||
"http://foo.bar.com": true,
|
||||
"http://fooubar.com": false,
|
||||
"http://barucom": false,
|
||||
"http://barcom": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "insensitive host simple",
|
||||
hostExp: "foo.bar.com",
|
||||
urls: map[string]bool{
|
||||
"http://FOO.bar.com": true,
|
||||
"http://foo.bar.com": true,
|
||||
"http://fooubar.com": false,
|
||||
"http://barucom": false,
|
||||
"http://barcom": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rt := &mux.Route{}
|
||||
err := hostRegexp(rt, test.hostExp)
|
||||
require.NoError(t, err)
|
||||
|
||||
for testURL, match := range test.urls {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, testURL, nil)
|
||||
assert.Equal(t, match, rt.Match(req, &mux.RouteMatch{}), testURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDomains(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
|
@ -914,21 +404,6 @@ func TestParseDomains(t *testing.T) {
|
|||
expression: "Foobar(`foo.bar`,`test.bar`)",
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
description: "Several host rules",
|
||||
expression: "Host(`foo.bar`,`test.bar`)",
|
||||
domain: []string{"foo.bar", "test.bar"},
|
||||
},
|
||||
{
|
||||
description: "Several host rules upper",
|
||||
expression: "HOST(`foo.bar`,`test.bar`)",
|
||||
domain: []string{"foo.bar", "test.bar"},
|
||||
},
|
||||
{
|
||||
description: "Several host rules lower",
|
||||
expression: "host(`foo.bar`,`test.bar`)",
|
||||
domain: []string{"foo.bar", "test.bar"},
|
||||
},
|
||||
{
|
||||
description: "No host rule",
|
||||
expression: "Path(`/test`)",
|
||||
|
@ -938,6 +413,11 @@ func TestParseDomains(t *testing.T) {
|
|||
expression: "Host(`foo.bar`) && Path(`/test`)",
|
||||
domain: []string{"foo.bar"},
|
||||
},
|
||||
{
|
||||
description: "Host rule to trim and another rule",
|
||||
expression: "Host(`Foo.Bar`) || Host(`bar.buz`) && Path(`/test`)",
|
||||
domain: []string{"foo.bar", "bar.buz"},
|
||||
},
|
||||
{
|
||||
description: "Host rule to trim and another rule",
|
||||
expression: "Host(`Foo.Bar`) && Path(`/test`)",
|
||||
|
@ -967,7 +447,9 @@ func TestParseDomains(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAbsoluteFormURL(t *testing.T) {
|
||||
// TestEmptyHost is a non regression test for
|
||||
// https://github.com/traefik/traefik/pull/9131
|
||||
func TestEmptyHost(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
request string
|
||||
|
@ -975,41 +457,41 @@ func TestAbsoluteFormURL(t *testing.T) {
|
|||
expected int
|
||||
}{
|
||||
{
|
||||
desc: "!HostRegexp with absolute-form URL with empty host with non-matching host header",
|
||||
request: "GET http://@/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n",
|
||||
rule: "!HostRegexp(`test.localhost`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "!Host with absolute-form URL with empty host with non-matching host header",
|
||||
request: "GET http://@/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n",
|
||||
rule: "!Host(`test.localhost`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "!HostRegexp with absolute-form URL with matching host header",
|
||||
request: "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n",
|
||||
rule: "!HostRegexp(`test.localhost`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "!Host with absolute-form URL with matching host header",
|
||||
request: "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n",
|
||||
rule: "!Host(`test.localhost`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "!HostRegexp with absolute-form URL with non-matching host header",
|
||||
request: "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n",
|
||||
rule: "!HostRegexp(`toto.localhost`)",
|
||||
desc: "HostRegexp with absolute-form URL with empty host with non-matching host header",
|
||||
request: "GET http://@/ HTTP/1.1\r\nHost: example.com\r\n\r\n",
|
||||
rule: "HostRegexp(`example.com`)",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "!Host with absolute-form URL with non-matching host header",
|
||||
request: "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n",
|
||||
rule: "!Host(`toto.localhost`)",
|
||||
desc: "Host with absolute-form URL with empty host with non-matching host header",
|
||||
request: "GET http://@/ HTTP/1.1\r\nHost: example.com\r\n\r\n",
|
||||
rule: "Host(`example.com`)",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "HostRegexp with absolute-form URL with matching host header",
|
||||
request: "GET http://example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n",
|
||||
rule: "HostRegexp(`example.com`)",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "Host with absolute-form URL with matching host header",
|
||||
request: "GET http://example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n",
|
||||
rule: "Host(`example.com`)",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "HostRegexp with absolute-form URL with non-matching host header",
|
||||
request: "GET http://example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n",
|
||||
rule: "HostRegexp(`example.org`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "Host with absolute-form URL with non-matching host header",
|
||||
request: "GET http://example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n",
|
||||
rule: "Host(`example.org`)",
|
||||
expected: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
|
134
pkg/muxer/tcp/matcher.go
Normal file
134
pkg/muxer/tcp/matcher.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v2/pkg/ip"
|
||||
)
|
||||
|
||||
var tcpFuncs = map[string]func(*matchersTree, ...string) error{
|
||||
"ALPN": expect1Parameter(alpn),
|
||||
"ClientIP": expect1Parameter(clientIP),
|
||||
"HostSNI": expect1Parameter(hostSNI),
|
||||
"HostSNIRegexp": expect1Parameter(hostSNIRegexp),
|
||||
}
|
||||
|
||||
func expect1Parameter(fn func(*matchersTree, ...string) error) func(*matchersTree, ...string) error {
|
||||
return func(route *matchersTree, s ...string) error {
|
||||
if len(s) != 1 {
|
||||
return fmt.Errorf("unexpected number of parameters; got %d, expected 1", len(s))
|
||||
}
|
||||
|
||||
return fn(route, s...)
|
||||
}
|
||||
}
|
||||
|
||||
// alpn checks if any of the connection ALPN protocols matches one of the matcher protocols.
|
||||
func alpn(tree *matchersTree, protos ...string) error {
|
||||
proto := protos[0]
|
||||
|
||||
if proto == tlsalpn01.ACMETLS1Protocol {
|
||||
return fmt.Errorf("invalid protocol value for ALPN matcher, %q is not allowed", proto)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
for _, alpnProto := range meta.alpnProtos {
|
||||
if alpnProto == proto {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func clientIP(tree *matchersTree, clientIP ...string) error {
|
||||
checker, err := ip.NewChecker(clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing IP checker for ClientIP matcher: %w", err)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
ok, err := checker.Contains(meta.remoteIP)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("ClientIP matcher: could not match remote address")
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var almostFQDN = regexp.MustCompile(`^[[:alnum:]\.-]+$`)
|
||||
|
||||
// hostSNI checks if the SNI Host of the connection match the matcher host.
|
||||
func hostSNI(tree *matchersTree, hosts ...string) error {
|
||||
host := hosts[0]
|
||||
|
||||
if host == "*" {
|
||||
// Since a HostSNI(`*`) rule has been provided as catchAll for non-TLS TCP,
|
||||
// it allows matching with an empty serverName.
|
||||
tree.matcher = func(meta ConnData) bool { return true }
|
||||
return nil
|
||||
}
|
||||
|
||||
if !almostFQDN.MatchString(host) {
|
||||
return fmt.Errorf("invalid value for HostSNI matcher, %q is not a valid hostname", host)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
if meta.serverName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if host == meta.serverName {
|
||||
return true
|
||||
}
|
||||
|
||||
// trim trailing period in case of FQDN
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
|
||||
return host == meta.serverName
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hostSNIRegexp checks if the SNI Host of the connection matches the matcher host regexp.
|
||||
func hostSNIRegexp(tree *matchersTree, templates ...string) error {
|
||||
template := templates[0]
|
||||
|
||||
if !isASCII(template) {
|
||||
return fmt.Errorf("invalid value for HostSNIRegexp matcher, %q is not a valid hostname", template)
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(template)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compiling HostSNIRegexp matcher: %w", err)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
return re.MatchString(meta.serverName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isASCII checks if the given string contains only ASCII characters.
|
||||
func isASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
383
pkg/muxer/tcp/matcher_test.go
Normal file
383
pkg/muxer/tcp/matcher_test.go
Normal file
|
@ -0,0 +1,383 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/tcp"
|
||||
)
|
||||
|
||||
func Test_HostSNICatchAll(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
isCatchAll bool
|
||||
}{
|
||||
{
|
||||
desc: "HostSNI(`example.com`) is not catchAll",
|
||||
rule: "HostSNI(`example.com`)",
|
||||
},
|
||||
{
|
||||
desc: "HostSNI(`*`) is catchAll",
|
||||
rule: "HostSNI(`*`)",
|
||||
isCatchAll: true,
|
||||
},
|
||||
{
|
||||
desc: "HostSNIRegexp(`^.*$`) is not catchAll",
|
||||
rule: "HostSNIRegexp(`.*`)",
|
||||
isCatchAll: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||
require.NoError(t, err)
|
||||
|
||||
handler, catchAll := muxer.Match(ConnData{
|
||||
serverName: "example.com",
|
||||
})
|
||||
require.NotNil(t, handler)
|
||||
assert.Equal(t, test.isCatchAll, catchAll)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_HostSNI(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
serverName string
|
||||
buildErr bool
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
desc: "Empty",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNI matcher (empty host)",
|
||||
rule: "HostSNI(``)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNI matcher (too many parameters)",
|
||||
rule: "HostSNI(`example.com`, `example.org`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNI matcher (globing sub domain)",
|
||||
rule: "HostSNI(`*.com`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNI matcher (non ASCII host)",
|
||||
rule: "HostSNI(`🦭.com`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid HostSNI matcher - puny-coded emoji",
|
||||
rule: "HostSNI(`xn--9t9h.com`)",
|
||||
serverName: "xn--9t9h.com",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid HostSNI matcher - puny-coded emoji but emoji in server name",
|
||||
rule: "HostSNI(`xn--9t9h.com`)",
|
||||
serverName: "🦭.com",
|
||||
},
|
||||
{
|
||||
desc: "Matching hosts",
|
||||
rule: "HostSNI(`example.com`)",
|
||||
serverName: "example.com",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "No matching hosts",
|
||||
rule: "HostSNI(`example.com`)",
|
||||
serverName: "example.org",
|
||||
},
|
||||
{
|
||||
desc: "Matching globing host `*`",
|
||||
rule: "HostSNI(`*`)",
|
||||
serverName: "example.com",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "Matching globing host `*` and empty server name",
|
||||
rule: "HostSNI(`*`)",
|
||||
serverName: "",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "Matching host with trailing dot",
|
||||
rule: "HostSNI(`example.com.`)",
|
||||
serverName: "example.com.",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "Matching host with trailing dot but not in server name",
|
||||
rule: "HostSNI(`example.com.`)",
|
||||
serverName: "example.com",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
desc: "Matching hosts with subdomains",
|
||||
rule: "HostSNI(`foo.example.com`)",
|
||||
serverName: "foo.example.com",
|
||||
match: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||
if test.buildErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
meta := ConnData{
|
||||
serverName: test.serverName,
|
||||
}
|
||||
|
||||
handler, _ := muxer.Match(meta)
|
||||
require.Equal(t, test.match, handler != nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_HostSNIRegexp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]bool
|
||||
buildErr bool
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
desc: "Empty",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNIRegexp matcher (empty host)",
|
||||
rule: "HostSNIRegexp(``)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNIRegexp matcher (non ASCII host)",
|
||||
rule: "HostSNIRegexp(`🦭.com`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNIRegexp matcher (invalid regexp)",
|
||||
rule: "HostSNIRegexp(`(example.com`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid HostSNIRegexp matcher (too many parameters)",
|
||||
rule: "HostSNIRegexp(`example.com`, `example.org`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "valid HostSNIRegexp matcher",
|
||||
rule: "HostSNIRegexp(`^example\\.(com|org)$`)",
|
||||
expected: map[string]bool{
|
||||
"example.com": true,
|
||||
"example.com.": false,
|
||||
"EXAMPLE.com": false,
|
||||
"example.org": true,
|
||||
"exampleuorg": false,
|
||||
"": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid HostSNIRegexp matcher with Traefik v2 syntax",
|
||||
rule: "HostSNIRegexp(`example.{tld:(com|org)}`)",
|
||||
expected: map[string]bool{
|
||||
"example.com": false,
|
||||
"example.com.": false,
|
||||
"EXAMPLE.com": false,
|
||||
"example.org": false,
|
||||
"exampleuorg": false,
|
||||
"": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||
if test.buildErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
for serverName, match := range test.expected {
|
||||
meta := ConnData{
|
||||
serverName: serverName,
|
||||
}
|
||||
|
||||
handler, _ := muxer.Match(meta)
|
||||
assert.Equal(t, match, handler != nil, serverName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ClientIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]bool
|
||||
buildErr bool
|
||||
}{
|
||||
{
|
||||
desc: "Empty",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ClientIP matcher (empty host)",
|
||||
rule: "ClientIP(``)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ClientIP matcher (non ASCII host)",
|
||||
rule: "ClientIP(`🦭/32`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ClientIP matcher (too many parameters)",
|
||||
rule: "ClientIP(`127.0.0.1`, `127.0.0.2`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "valid ClientIP matcher",
|
||||
rule: "ClientIP(`20.20.20.20`)",
|
||||
expected: map[string]bool{
|
||||
"20.20.20.20": true,
|
||||
"10.10.10.10": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid ClientIP matcher with CIDR",
|
||||
rule: "ClientIP(`20.20.20.20/24`)",
|
||||
expected: map[string]bool{
|
||||
"20.20.20.20": true,
|
||||
"20.20.20.40": true,
|
||||
"10.10.10.10": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||
if test.buildErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
for remoteIP, match := range test.expected {
|
||||
meta := ConnData{
|
||||
remoteIP: remoteIP,
|
||||
}
|
||||
|
||||
handler, _ := muxer.Match(meta)
|
||||
assert.Equal(t, match, handler != nil, remoteIP)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ALPN(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rule string
|
||||
expected map[string]bool
|
||||
buildErr bool
|
||||
}{
|
||||
{
|
||||
desc: "Empty",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ALPN matcher (TLS proto)",
|
||||
rule: "ALPN(`acme-tls/1`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ALPN matcher (empty parameters)",
|
||||
rule: "ALPN(``)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid ALPN matcher (too many parameters)",
|
||||
rule: "ALPN(`h2`, `mqtt`)",
|
||||
buildErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid ALPN matcher",
|
||||
rule: "ALPN(`h2`)",
|
||||
expected: map[string]bool{
|
||||
"h2": true,
|
||||
"mqtt": false,
|
||||
"": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||
if test.buildErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
for proto, match := range test.expected {
|
||||
meta := ConnData{
|
||||
alpnProtos: []string{proto},
|
||||
}
|
||||
|
||||
handler, _ := muxer.Match(meta)
|
||||
assert.Equal(t, match, handler != nil, proto)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,31 +1,18 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v2/pkg/ip"
|
||||
"github.com/traefik/traefik/v2/pkg/rules"
|
||||
"github.com/traefik/traefik/v2/pkg/tcp"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/vulcand/predicate"
|
||||
)
|
||||
|
||||
var tcpFuncs = map[string]func(*matchersTree, ...string) error{
|
||||
"HostSNI": hostSNI,
|
||||
"HostSNIRegexp": hostSNIRegexp,
|
||||
"ClientIP": clientIP,
|
||||
"ALPN": alpn,
|
||||
}
|
||||
|
||||
// ParseHostSNI extracts the HostSNIs declared in a rule.
|
||||
// This is a first naive implementation used in TCP routing.
|
||||
func ParseHostSNI(rule string) ([]string, error) {
|
||||
|
@ -261,233 +248,7 @@ func (m *matchersTree) match(meta ConnData) bool {
|
|||
return m.left.match(meta) && m.right.match(meta)
|
||||
default:
|
||||
// This should never happen as it should have been detected during parsing.
|
||||
log.Warn().Msgf("Invalid rule operator %s", m.operator)
|
||||
log.Warn().Str("operator", m.operator).Msg("Invalid rule operator")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func clientIP(tree *matchersTree, clientIPs ...string) error {
|
||||
checker, err := ip.NewChecker(clientIPs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
if meta.remoteIP == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
ok, err := checker.Contains(meta.remoteIP)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// alpn checks if any of the connection ALPN protocols matches one of the matcher protocols.
|
||||
func alpn(tree *matchersTree, protos ...string) error {
|
||||
if len(protos) == 0 {
|
||||
return errors.New("empty value for \"ALPN\" matcher is not allowed")
|
||||
}
|
||||
|
||||
for _, proto := range protos {
|
||||
if proto == tlsalpn01.ACMETLS1Protocol {
|
||||
return fmt.Errorf("invalid protocol value for \"ALPN\" matcher, %q is not allowed", proto)
|
||||
}
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
for _, proto := range meta.alpnProtos {
|
||||
for _, filter := range protos {
|
||||
if proto == filter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var almostFQDN = regexp.MustCompile(`^[[:alnum:]\.-]+$`)
|
||||
|
||||
// hostSNI checks if the SNI Host of the connection match the matcher host.
|
||||
func hostSNI(tree *matchersTree, hosts ...string) error {
|
||||
if len(hosts) == 0 {
|
||||
return errors.New("empty value for \"HostSNI\" matcher is not allowed")
|
||||
}
|
||||
|
||||
for i, host := range hosts {
|
||||
// Special case to allow global wildcard
|
||||
if host == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !almostFQDN.MatchString(host) {
|
||||
return fmt.Errorf("invalid value for \"HostSNI\" matcher, %q is not a valid hostname", host)
|
||||
}
|
||||
|
||||
hosts[i] = strings.ToLower(host)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
// Since a HostSNI(`*`) rule has been provided as catchAll for non-TLS TCP,
|
||||
// it allows matching with an empty serverName.
|
||||
// Which is why we make sure to take that case into account before
|
||||
// checking meta.serverName.
|
||||
if hosts[0] == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
if meta.serverName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
if host == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
if host == meta.serverName {
|
||||
return true
|
||||
}
|
||||
|
||||
// trim trailing period in case of FQDN
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
if host == meta.serverName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hostSNIRegexp checks if the SNI Host of the connection matches the matcher host regexp.
|
||||
func hostSNIRegexp(tree *matchersTree, templates ...string) error {
|
||||
if len(templates) == 0 {
|
||||
return fmt.Errorf("empty value for \"HostSNIRegexp\" matcher is not allowed")
|
||||
}
|
||||
|
||||
var regexps []*regexp.Regexp
|
||||
|
||||
for _, template := range templates {
|
||||
preparedPattern, err := preparePattern(template)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid pattern value for \"HostSNIRegexp\" matcher, %q is not a valid pattern: %w", template, err)
|
||||
}
|
||||
|
||||
regexp, err := regexp.Compile(preparedPattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
regexps = append(regexps, regexp)
|
||||
}
|
||||
|
||||
tree.matcher = func(meta ConnData) bool {
|
||||
for _, regexp := range regexps {
|
||||
if regexp.MatchString(meta.serverName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: expose more of containous/mux fork to get rid of the following copied code (https://github.com/containous/mux/blob/8ffa4f6d063c/regexp.go).
|
||||
|
||||
// preparePattern builds a regexp pattern from the initial user defined expression.
|
||||
// This function reuses the code dedicated to host matching of the newRouteRegexp func from the gorilla/mux library.
|
||||
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||
func preparePattern(template string) (string, error) {
|
||||
// Check if it is well-formed.
|
||||
idxs, errBraces := braceIndices(template)
|
||||
if errBraces != nil {
|
||||
return "", errBraces
|
||||
}
|
||||
|
||||
defaultPattern := "[^.]+"
|
||||
pattern := bytes.NewBufferString("")
|
||||
|
||||
// Host SNI matching is case-insensitive
|
||||
fmt.Fprint(pattern, "(?i)")
|
||||
|
||||
pattern.WriteByte('^')
|
||||
var end int
|
||||
var err error
|
||||
for i := 0; i < len(idxs); i += 2 {
|
||||
// Set all values we are interested in.
|
||||
raw := template[end:idxs[i]]
|
||||
end = idxs[i+1]
|
||||
parts := strings.SplitN(template[idxs[i]+1:end-1], ":", 2)
|
||||
name := parts[0]
|
||||
patt := defaultPattern
|
||||
if len(parts) == 2 {
|
||||
patt = parts[1]
|
||||
}
|
||||
// Name or pattern can't be empty.
|
||||
if name == "" || patt == "" {
|
||||
return "", fmt.Errorf("mux: missing name or pattern in %q",
|
||||
template[idxs[i]:end])
|
||||
}
|
||||
// Build the regexp pattern.
|
||||
fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
|
||||
|
||||
// Append variable name and compiled pattern.
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Add the remaining.
|
||||
raw := template[end:]
|
||||
pattern.WriteString(regexp.QuoteMeta(raw))
|
||||
pattern.WriteByte('$')
|
||||
|
||||
return pattern.String(), nil
|
||||
}
|
||||
|
||||
// varGroupName builds a capturing group name for the indexed variable.
|
||||
// This function is a copy of varGroupName func from the gorilla/mux library.
|
||||
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||
func varGroupName(idx int) string {
|
||||
return "v" + strconv.Itoa(idx)
|
||||
}
|
||||
|
||||
// braceIndices returns the first level curly brace indices from a string.
|
||||
// This function is a copy of braceIndices func from the gorilla/mux library.
|
||||
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||
func braceIndices(s string) ([]int, error) {
|
||||
var level, idx int
|
||||
var idxs []int
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '{':
|
||||
if level++; level == 1 {
|
||||
idx = i
|
||||
}
|
||||
case '}':
|
||||
if level--; level == 0 {
|
||||
idxs = append(idxs, idx, i+1)
|
||||
} else if level < 0 {
|
||||
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if level != 0 {
|
||||
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||
}
|
||||
return idxs, nil
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -233,12 +233,12 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to consul catalog server")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -329,11 +329,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to docker server")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -183,11 +183,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), routineCtx), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to Provider api")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -97,11 +97,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to server endpoint")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ func TestProvider_decodeConfiguration(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "should return the decoded dynamic configuration",
|
||||
configData: []byte("{\"tcp\":{\"routers\":{\"foo\":{}}}}"),
|
||||
configData: []byte(`{"tcp":{"routers":{"foo":{}}}}`),
|
||||
expConfig: &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
|
|
|
@ -101,7 +101,7 @@ func patchDynamicConfiguration(cfg *dynamic.Configuration, ep string, port int,
|
|||
cfg.HTTP.Routers["traefik-hub-agent-service"] = &dynamic.Router{
|
||||
EntryPoints: []string{ep},
|
||||
Service: "traefik-hub-agent-service",
|
||||
Rule: "Host(`proxy.traefik`) && PathPrefix(`/config`, `/discover-ip`, `/state`)",
|
||||
Rule: "Host(`proxy.traefik`) && (PathPrefix(`/config`) || PathPrefix(`/discover-ip`) || PathPrefix(`/state`))",
|
||||
}
|
||||
|
||||
cfg.HTTP.Services["traefik-hub-agent-service"] = &dynamic.Service{
|
||||
|
|
|
@ -168,11 +168,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to Provider")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -13,72 +13,72 @@ type IngressRouteSpec struct {
|
|||
Routes []Route `json:"routes"`
|
||||
// EntryPoints defines the list of entry point names to bind to.
|
||||
// Entry points have to be configured in the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
// Default: all.
|
||||
EntryPoints []string `json:"entryPoints,omitempty"`
|
||||
// TLS defines the TLS configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls
|
||||
TLS *TLS `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
// Route holds the HTTP route configuration.
|
||||
type Route struct {
|
||||
// Match defines the router's rule.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule
|
||||
Match string `json:"match"`
|
||||
// Kind defines the kind of the route.
|
||||
// Rule is the only supported kind.
|
||||
// +kubebuilder:validation:Enum=Rule
|
||||
Kind string `json:"kind"`
|
||||
// Priority defines the router's priority.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority
|
||||
Priority int `json:"priority,omitempty"`
|
||||
// Services defines the list of Service.
|
||||
// It can contain any combination of TraefikService and/or reference to a Kubernetes Service.
|
||||
Services []Service `json:"services,omitempty"`
|
||||
// Middlewares defines the list of references to Middleware resources.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-middleware
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware
|
||||
Middlewares []MiddlewareRef `json:"middlewares,omitempty"`
|
||||
}
|
||||
|
||||
// TLS holds the TLS configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls
|
||||
type TLS struct {
|
||||
// SecretName is the name of the referenced Kubernetes Secret to specify the certificate details.
|
||||
SecretName string `json:"secretName,omitempty"`
|
||||
// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection.
|
||||
// If not defined, the `default` TLSOption is used.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options
|
||||
Options *TLSOptionRef `json:"options,omitempty"`
|
||||
// Store defines the reference to the TLSStore, that will be used to store certificates.
|
||||
// Please note that only `default` TLSStore can be used.
|
||||
Store *TLSStoreRef `json:"store,omitempty"`
|
||||
// CertResolver defines the name of the certificate resolver to use.
|
||||
// Cert resolvers have to be configured in the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers
|
||||
CertResolver string `json:"certResolver,omitempty"`
|
||||
// Domains defines the list of domains that will be used to issue certificates.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains
|
||||
Domains []types.Domain `json:"domains,omitempty"`
|
||||
}
|
||||
|
||||
// TLSOptionRef is a reference to a TLSOption resource.
|
||||
type TLSOptionRef struct {
|
||||
// Name defines the name of the referenced TLSOption.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption
|
||||
Name string `json:"name"`
|
||||
// Namespace defines the namespace of the referenced TLSOption.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsoption
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// TLSStoreRef is a reference to a TLSStore resource.
|
||||
type TLSStoreRef struct {
|
||||
// Name defines the name of the referenced TLSStore.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore
|
||||
Name string `json:"name"`
|
||||
// Namespace defines the namespace of the referenced TLSStore.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-tlsstore
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ type LoadBalancerSpec struct {
|
|||
// Namespace defines the namespace of the referenced Kubernetes Service or TraefikService.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// Sticky defines the sticky sessions configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#sticky-sessions
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions
|
||||
Sticky *dynamic.Sticky `json:"sticky,omitempty"`
|
||||
// Port defines the port of a Kubernetes Service.
|
||||
// This can be a reference to a named port.
|
||||
|
|
|
@ -13,21 +13,21 @@ type IngressRouteTCPSpec struct {
|
|||
Routes []RouteTCP `json:"routes"`
|
||||
// EntryPoints defines the list of entry point names to bind to.
|
||||
// Entry points have to be configured in the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
// Default: all.
|
||||
EntryPoints []string `json:"entryPoints,omitempty"`
|
||||
// TLS defines the TLS configuration on a layer 4 / TCP Route.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls_1
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1
|
||||
TLS *TLSTCP `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
// RouteTCP holds the TCP route configuration.
|
||||
type RouteTCP struct {
|
||||
// Match defines the router's rule.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule_1
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1
|
||||
Match string `json:"match"`
|
||||
// Priority defines the router's priority.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#priority_1
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1
|
||||
Priority int `json:"priority,omitempty"`
|
||||
// Services defines the list of TCP services.
|
||||
Services []ServiceTCP `json:"services,omitempty"`
|
||||
|
@ -36,7 +36,7 @@ type RouteTCP struct {
|
|||
}
|
||||
|
||||
// TLSTCP holds the TLS configuration for an IngressRouteTCP.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#tls_1
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1
|
||||
type TLSTCP struct {
|
||||
// SecretName is the name of the referenced Kubernetes Secret to specify the certificate details.
|
||||
SecretName string `json:"secretName,omitempty"`
|
||||
|
@ -44,17 +44,17 @@ type TLSTCP struct {
|
|||
Passthrough bool `json:"passthrough,omitempty"`
|
||||
// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection.
|
||||
// If not defined, the `default` TLSOption is used.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options
|
||||
Options *ObjectReference `json:"options,omitempty"`
|
||||
// Store defines the reference to the TLSStore, that will be used to store certificates.
|
||||
// Please note that only `default` TLSStore can be used.
|
||||
Store *ObjectReference `json:"store,omitempty"`
|
||||
// CertResolver defines the name of the certificate resolver to use.
|
||||
// Cert resolvers have to be configured in the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/acme/#certificate-resolvers
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers
|
||||
CertResolver string `json:"certResolver,omitempty"`
|
||||
// Domains defines the list of domains that will be used to issue certificates.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#domains
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains
|
||||
Domains []types.Domain `json:"domains,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ type ServiceTCP struct {
|
|||
// A negative value means an infinite deadline (i.e. the reading capability is never closed).
|
||||
TerminationDelay *int `json:"terminationDelay,omitempty"`
|
||||
// ProxyProtocol defines the PROXY protocol configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol
|
||||
ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ type IngressRouteUDPSpec struct {
|
|||
Routes []RouteUDP `json:"routes"`
|
||||
// EntryPoints defines the list of entry point names to bind to.
|
||||
// Entry points have to be configured in the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/entrypoints/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/
|
||||
// Default: all.
|
||||
EntryPoints []string `json:"entryPoints,omitempty"`
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
// +kubebuilder:storageversion
|
||||
|
||||
// Middleware is the CRD implementation of a Traefik Middleware.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/overview/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/overview/
|
||||
type Middleware struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
@ -58,7 +58,7 @@ type MiddlewareSpec struct {
|
|||
|
||||
// ErrorPage holds the custom error middleware configuration.
|
||||
// This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/
|
||||
type ErrorPage struct {
|
||||
// Status defines which status or range of statuses should result in an error page.
|
||||
// It can be either a status code as a number (500),
|
||||
|
@ -67,7 +67,7 @@ type ErrorPage struct {
|
|||
// or a combination of the two (404,418,500-599).
|
||||
Status []string `json:"status,omitempty"`
|
||||
// Service defines the reference to a Kubernetes Service that will serve the error page.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/errorpages/#service
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service
|
||||
Service Service `json:"service,omitempty"`
|
||||
// Query defines the URL for the error page (hosted by service).
|
||||
// The {status} variable can be used in order to insert the status code in the URL.
|
||||
|
@ -92,7 +92,7 @@ type CircuitBreaker struct {
|
|||
|
||||
// Chain holds the configuration of the chain middleware.
|
||||
// This middleware enables to define reusable combinations of other pieces of middleware.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/chain/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/
|
||||
type Chain struct {
|
||||
// Middlewares is the list of MiddlewareRef which composes the chain.
|
||||
Middlewares []MiddlewareRef `json:"middlewares,omitempty"`
|
||||
|
@ -102,7 +102,7 @@ type Chain struct {
|
|||
|
||||
// BasicAuth holds the basic auth middleware configuration.
|
||||
// This middleware restricts access to your services to known users.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/
|
||||
type BasicAuth struct {
|
||||
// Secret is the name of the referenced Kubernetes Secret containing user credentials.
|
||||
Secret string `json:"secret,omitempty"`
|
||||
|
@ -113,7 +113,7 @@ type BasicAuth struct {
|
|||
// Default: false.
|
||||
RemoveHeader bool `json:"removeHeader,omitempty"`
|
||||
// HeaderField defines a header field to store the authenticated user.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield
|
||||
HeaderField string `json:"headerField,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ type BasicAuth struct {
|
|||
|
||||
// DigestAuth holds the digest auth middleware configuration.
|
||||
// This middleware restricts access to your services to known users.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/digestauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/
|
||||
type DigestAuth struct {
|
||||
// Secret is the name of the referenced Kubernetes Secret containing user credentials.
|
||||
Secret string `json:"secret,omitempty"`
|
||||
|
@ -131,7 +131,7 @@ type DigestAuth struct {
|
|||
// Default: traefik.
|
||||
Realm string `json:"realm,omitempty"`
|
||||
// HeaderField defines a header field to store the authenticated user.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/basicauth/#headerfield
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield
|
||||
HeaderField string `json:"headerField,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ type DigestAuth struct {
|
|||
|
||||
// ForwardAuth holds the forward auth middleware configuration.
|
||||
// This middleware delegates the request authentication to a Service.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/
|
||||
type ForwardAuth struct {
|
||||
// Address defines the authentication server address.
|
||||
Address string `json:"address,omitempty"`
|
||||
|
@ -148,7 +148,7 @@ type ForwardAuth struct {
|
|||
// AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers.
|
||||
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"`
|
||||
// AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/forwardauth/#authresponseheadersregex
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex
|
||||
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty"`
|
||||
// AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server.
|
||||
// If not set or empty then all request headers are passed.
|
||||
|
@ -173,7 +173,7 @@ type ClientTLS struct {
|
|||
|
||||
// RateLimit holds the rate limit configuration.
|
||||
// This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/ratelimit/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/
|
||||
type RateLimit struct {
|
||||
// Average is the maximum rate, by default in requests/s, allowed for the given source.
|
||||
// It defaults to 0, which means no rate limiting.
|
||||
|
@ -197,7 +197,7 @@ type RateLimit struct {
|
|||
// Retry holds the retry middleware configuration.
|
||||
// This middleware reissues requests a given number of times to a backend server if that server does not reply.
|
||||
// As soon as the server answers, the middleware stops retrying, regardless of the response status.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/retry/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/
|
||||
type Retry struct {
|
||||
// Attempts defines how many times the request should be retried.
|
||||
Attempts int `json:"attempts,omitempty"`
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// MiddlewareTCP is the CRD implementation of a Traefik TCP middleware.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/overview/
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/middlewares/overview/
|
||||
type MiddlewareTCP struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// ServersTransport is the CRD implementation of a ServersTransport.
|
||||
// If no serversTransport is specified, the default@internal will be used.
|
||||
// The default@internal serversTransport is created from the static configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#serverstransport_1
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_1
|
||||
type ServersTransport struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// TraefikService object allows to:
|
||||
// - Apply weight to Services on load-balancing
|
||||
// - Mirror traffic on services
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#kind-traefikservice
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-traefikservice
|
||||
type TraefikService struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
@ -49,7 +49,7 @@ type TraefikServiceSpec struct {
|
|||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// Mirroring holds the mirroring service configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#mirroring-service
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#mirroring-service
|
||||
type Mirroring struct {
|
||||
LoadBalancerSpec `json:",inline"`
|
||||
|
||||
|
@ -75,11 +75,11 @@ type MirrorService struct {
|
|||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// WeightedRoundRobin holds the weighted round-robin configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/services/#weighted-round-robin-service
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#weighted-round-robin-service
|
||||
type WeightedRoundRobin struct {
|
||||
// Services defines the list of Kubernetes Service and/or TraefikService to load-balance, with weight.
|
||||
Services []Service `json:"services,omitempty"`
|
||||
// Sticky defines whether sticky sessions are enabled.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/routing/providers/kubernetes-crd/#stickiness-and-load-balancing
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#stickiness-and-load-balancing
|
||||
Sticky *dynamic.Sticky `json:"sticky,omitempty"`
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
// +kubebuilder:storageversion
|
||||
|
||||
// TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#tls-options
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options
|
||||
type TLSOption struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
@ -32,17 +32,17 @@ type TLSOptionSpec struct {
|
|||
// Default: None.
|
||||
MaxVersion string `json:"maxVersion,omitempty"`
|
||||
// CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#cipher-suites
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#cipher-suites
|
||||
CipherSuites []string `json:"cipherSuites,omitempty"`
|
||||
// CurvePreferences defines the preferred elliptic curves in a specific order.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#curve-preferences
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#curve-preferences
|
||||
CurvePreferences []string `json:"curvePreferences,omitempty"`
|
||||
// ClientAuth defines the server's policy for TLS Client Authentication.
|
||||
ClientAuth ClientAuth `json:"clientAuth,omitempty"`
|
||||
// SniStrict defines whether Traefik allows connections from clients connections that do not specify a server_name extension.
|
||||
SniStrict bool `json:"sniStrict,omitempty"`
|
||||
// ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#alpn-protocols
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#alpn-protocols
|
||||
ALPNProtocols []string `json:"alpnProtocols,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
// TLSStore is the CRD implementation of a Traefik TLS Store.
|
||||
// For the time being, only the TLSStore named default is supported.
|
||||
// This means that you cannot have two stores that are named default in different Kubernetes namespaces.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/https/tls/#certificates-stores
|
||||
// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#certificates-stores
|
||||
type TLSStore struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -161,11 +162,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to Provider")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1163,8 +1164,7 @@ func getRouteBindingSelectorNamespace(client Client, gatewayNamespace string, ro
|
|||
}
|
||||
|
||||
func hostRule(hostnames []v1alpha2.Hostname) (string, error) {
|
||||
var hostNames []string
|
||||
var hostRegexNames []string
|
||||
var rules []string
|
||||
|
||||
for _, hostname := range hostnames {
|
||||
host := string(hostname)
|
||||
|
@ -1177,7 +1177,7 @@ func hostRule(hostnames []v1alpha2.Hostname) (string, error) {
|
|||
|
||||
wildcard := strings.Count(host, "*")
|
||||
if wildcard == 0 {
|
||||
hostNames = append(hostNames, host)
|
||||
rules = append(rules, fmt.Sprintf("Host(`%s`)", host))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1186,25 +1186,18 @@ func hostRule(hostnames []v1alpha2.Hostname) (string, error) {
|
|||
return "", fmt.Errorf("invalid rule: %q", host)
|
||||
}
|
||||
|
||||
hostRegexNames = append(hostRegexNames, strings.Replace(host, "*.", "{subdomain:[a-zA-Z0-9-]+}.", 1))
|
||||
host = strings.Replace(regexp.QuoteMeta(host), `\*\.`, `[a-zA-Z0-9-]+\.`, 1)
|
||||
rules = append(rules, fmt.Sprintf("HostRegexp(`^%s$`)", host))
|
||||
}
|
||||
|
||||
var res string
|
||||
if len(hostNames) > 0 {
|
||||
res = "Host(`" + strings.Join(hostNames, "`, `") + "`)"
|
||||
switch len(rules) {
|
||||
case 0:
|
||||
return "", nil
|
||||
case 1:
|
||||
return rules[0], nil
|
||||
default:
|
||||
return fmt.Sprintf("(%s)", strings.Join(rules, " || ")), nil
|
||||
}
|
||||
|
||||
if len(hostRegexNames) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
hostRegexp := "HostRegexp(`" + strings.Join(hostRegexNames, "`, `") + "`)"
|
||||
|
||||
if len(res) > 0 {
|
||||
return "(" + res + " || " + hostRegexp + ")", nil
|
||||
}
|
||||
|
||||
return hostRegexp, nil
|
||||
}
|
||||
|
||||
func hostSNIRule(hostnames []v1alpha2.Hostname) (string, error) {
|
||||
|
@ -1227,15 +1220,18 @@ func hostSNIRule(hostnames []v1alpha2.Hostname) (string, error) {
|
|||
return "", fmt.Errorf("wildcard hostname is not supported: %q", h)
|
||||
}
|
||||
|
||||
matchers = append(matchers, "`"+h+"`")
|
||||
matchers = append(matchers, fmt.Sprintf("HostSNI(`%s`)", h))
|
||||
uniqHostnames[hostname] = struct{}{}
|
||||
}
|
||||
|
||||
if len(matchers) == 0 {
|
||||
switch len(matchers) {
|
||||
case 0:
|
||||
return "HostSNI(`*`)", nil
|
||||
case 1:
|
||||
return matchers[0], nil
|
||||
default:
|
||||
return fmt.Sprintf("(%s)", strings.Join(matchers, " || ")), nil
|
||||
}
|
||||
|
||||
return "HostSNI(" + strings.Join(matchers, ",") + ")", nil
|
||||
}
|
||||
|
||||
func extractRule(routeRule v1alpha2.HTTPRouteRule, hostRule string) (string, error) {
|
||||
|
|
|
@ -744,15 +744,15 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-http-app-1-my-gateway-web-75dd1ad561e42725558a": {
|
||||
"default-http-app-1-my-gateway-web-66e726cd8903b49727ae": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "default-http-app-1-my-gateway-web-75dd1ad561e42725558a-wrr",
|
||||
Rule: "Host(`foo.com`, `bar.com`) && PathPrefix(`/`)",
|
||||
Service: "default-http-app-1-my-gateway-web-66e726cd8903b49727ae-wrr",
|
||||
Rule: "(Host(`foo.com`) || Host(`bar.com`)) && PathPrefix(`/`)",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-http-app-1-my-gateway-web-75dd1ad561e42725558a-wrr": {
|
||||
"default-http-app-1-my-gateway-web-66e726cd8903b49727ae-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
|
@ -802,15 +802,15 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-http-app-1-my-gateway-web-2dbd7883f5537db39bca": {
|
||||
"default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "default-http-app-1-my-gateway-web-2dbd7883f5537db39bca-wrr",
|
||||
Rule: "(Host(`foo.com`) || HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.bar.com`)) && PathPrefix(`/`)",
|
||||
Service: "default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0-wrr",
|
||||
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.com$`)) && PathPrefix(`/`)",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-http-app-1-my-gateway-web-2dbd7883f5537db39bca-wrr": {
|
||||
"default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
|
@ -860,15 +860,15 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-http-app-1-my-gateway-web-eb1490f180299bf5ed29": {
|
||||
"default-http-app-1-my-gateway-web-b0521a61fb43068694b4": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "default-http-app-1-my-gateway-web-eb1490f180299bf5ed29-wrr",
|
||||
Rule: "(Host(`foo.com`) || HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.foo.com`)) && PathPrefix(`/`)",
|
||||
Service: "default-http-app-1-my-gateway-web-b0521a61fb43068694b4-wrr",
|
||||
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.foo\\.com$`)) && PathPrefix(`/`)",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-http-app-1-my-gateway-web-eb1490f180299bf5ed29-wrr": {
|
||||
"default-http-app-1-my-gateway-web-b0521a61fb43068694b4-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
|
@ -3011,10 +3011,10 @@ func TestLoadTLSRoutes(t *testing.T) {
|
|||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{
|
||||
"default-tls-app-1-my-gateway-tls-339184c3296a9c2c39fa": {
|
||||
"default-tls-app-1-my-gateway-tls-dfc5c7506ac1b172c8b7": {
|
||||
EntryPoints: []string{"tls"},
|
||||
Service: "default-tls-app-1-my-gateway-tls-339184c3296a9c2c39fa-wrr-0",
|
||||
Rule: "HostSNI(`foo.example.com`,`bar.example.com`)",
|
||||
Service: "default-tls-app-1-my-gateway-tls-dfc5c7506ac1b172c8b7-wrr-0",
|
||||
Rule: "(HostSNI(`foo.example.com`) || HostSNI(`bar.example.com`))",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{
|
||||
Passthrough: true,
|
||||
},
|
||||
|
@ -3022,7 +3022,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
|||
},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{
|
||||
"default-tls-app-1-my-gateway-tls-339184c3296a9c2c39fa-wrr-0": {
|
||||
"default-tls-app-1-my-gateway-tls-dfc5c7506ac1b172c8b7-wrr-0": {
|
||||
Weighted: &dynamic.TCPWeightedRoundRobin{
|
||||
Services: []dynamic.TCPWRRService{
|
||||
{
|
||||
|
@ -4362,7 +4362,7 @@ func Test_hostRule(t *testing.T) {
|
|||
"Bar",
|
||||
"Bir",
|
||||
},
|
||||
expectedRule: "Host(`Foo`, `Bar`, `Bir`)",
|
||||
expectedRule: "(Host(`Foo`) || Host(`Bar`) || Host(`Bir`))",
|
||||
},
|
||||
{
|
||||
desc: "Multiple Hosts with empty one",
|
||||
|
@ -4389,14 +4389,14 @@ func Test_hostRule(t *testing.T) {
|
|||
"bar.foo",
|
||||
"foo.foo",
|
||||
},
|
||||
expectedRule: "(Host(`bar.foo`, `foo.foo`) || HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.bar.foo`))",
|
||||
expectedRule: "(HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.foo$`) || Host(`bar.foo`) || Host(`foo.foo`))",
|
||||
},
|
||||
{
|
||||
desc: "Host with wildcard",
|
||||
hostnames: []v1alpha2.Hostname{
|
||||
"*.bar.foo",
|
||||
},
|
||||
expectedRule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.bar.foo`)",
|
||||
expectedRule: "HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.foo$`)",
|
||||
},
|
||||
{
|
||||
desc: "Alone wildcard",
|
||||
|
@ -4708,7 +4708,7 @@ func Test_hostSNIRule(t *testing.T) {
|
|||
{
|
||||
desc: "Some empty hostnames",
|
||||
hostnames: []v1alpha2.Hostname{"foo", "", "bar"},
|
||||
expectedRule: "HostSNI(`foo`,`bar`)",
|
||||
expectedRule: "(HostSNI(`foo`) || HostSNI(`bar`))",
|
||||
},
|
||||
{
|
||||
desc: "Valid hostname",
|
||||
|
@ -4718,12 +4718,12 @@ func Test_hostSNIRule(t *testing.T) {
|
|||
{
|
||||
desc: "Multiple valid hostnames",
|
||||
hostnames: []v1alpha2.Hostname{"foo", "bar"},
|
||||
expectedRule: "HostSNI(`foo`,`bar`)",
|
||||
expectedRule: "(HostSNI(`foo`) || HostSNI(`bar`))",
|
||||
},
|
||||
{
|
||||
desc: "Multiple overlapping hostnames",
|
||||
hostnames: []v1alpha2.Hostname{"foo", "bar", "foo", "baz"},
|
||||
expectedRule: "HostSNI(`foo`,`bar`,`baz`)",
|
||||
expectedRule: "(HostSNI(`foo`) || HostSNI(`bar`) || HostSNI(`baz`))",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -168,12 +169,12 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
|
||||
logger.Error().Err(err).Msgf("Provider error, retrying in %s", time)
|
||||
}
|
||||
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Cannot connect to Provider")
|
||||
logger.Error().Err(err).Msg("Cannot retrieve data")
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -400,10 +401,11 @@ func (p *Provider) shouldProcessIngress(ingress *networkingv1.Ingress, ingressCl
|
|||
|
||||
func buildHostRule(host string) string {
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
return "HostRegexp(`" + strings.Replace(host, "*.", "{subdomain:[a-zA-Z0-9-]+}.", 1) + "`)"
|
||||
host = strings.Replace(regexp.QuoteMeta(host), `\*\.`, `[a-zA-Z0-9-]+\.`, 1)
|
||||
return fmt.Sprintf("HostRegexp(`^%s$`)", host)
|
||||
}
|
||||
|
||||
return "Host(`" + host + "`)"
|
||||
return fmt.Sprintf("Host(`%s`)", host)
|
||||
}
|
||||
|
||||
func getCertificates(ctx context.Context, ingress *networkingv1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
|
||||
|
|
|
@ -189,8 +189,8 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
|||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"testing-bar-bar-3be6cfd7daba66cf2fdd": {
|
||||
Rule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.bar`) && PathPrefix(`/bar`)",
|
||||
"testing-bar-bar-aba9a7d00e9b06a78e16": {
|
||||
Rule: "HostRegexp(`^[a-zA-Z0-9-]+\\.bar$`) && PathPrefix(`/bar`)",
|
||||
Service: "testing-service1-80",
|
||||
},
|
||||
"testing-bar-bar-636bf36c00fedaab3d44": {
|
||||
|
@ -1104,7 +1104,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
|||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"testing-foobar-com-bar": {
|
||||
Rule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.foobar.com`) && PathPrefix(`/bar`)",
|
||||
Rule: "HostRegexp(`^[a-zA-Z0-9-]+\\.foobar\\.com$`) && PathPrefix(`/bar`)",
|
||||
Service: "testing-service1-80",
|
||||
},
|
||||
},
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue