diff --git a/.golangci.yml b/.golangci.yml index cf35a0d8a..8d811008d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -217,7 +217,7 @@ issues: exclude: - 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' - "should have a package comment, unless it's in another file for this package" - - 'fmt.Sprintf can be replaced with string addition' + - 'fmt.Sprintf can be replaced with string' exclude-rules: - path: '(.+)_test.go' linters: diff --git a/.goreleaser.yml.tmpl b/.goreleaser.yml.tmpl index b8320bcbd..5deda441e 100644 --- a/.goreleaser.yml.tmpl +++ b/.goreleaser.yml.tmpl @@ -46,7 +46,7 @@ builds: goarch: arm changelog: - skip: true + disable: true archives: - id: traefik diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index bbbb8d239..b664bbe3f 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -25,7 +25,7 @@ global_job_config: - export "PATH=${GOPATH}/bin:${PATH}" - mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin" - export GOPROXY=https://proxy.golang.org,direct - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.56.0 + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.56.2 - curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin" - checkout - cache restore traefik-$(checksum go.sum) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb820d3ea..1c5e1a28c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +## [v3.0.0-rc3](https://github.com/traefik/traefik/tree/v3.0.0-rc3) (2024-03-13) +[All Commits](https://github.com/traefik/traefik/compare/v3.0.0-rc2...v3.0.0-rc3) + +**Misc:** +- Merge branch v2.11 into v3.0 ([#10519](https://github.com/traefik/traefik/pull/10519) by [rtribotte](https://github.com/rtribotte)) + +## [v3.0.0-rc2](https://github.com/traefik/traefik/tree/v3.0.0-rc2) (2024-03-12) +[All Commits](https://github.com/traefik/traefik/compare/v3.0.0-rc1...v3.0.0-rc2) + +**Enhancements:** +- **[consul]** ConsulCatalog StrictChecks ([#10388](https://github.com/traefik/traefik/pull/10388) by [djenriquez](https://github.com/djenriquez)) +- **[metrics,tracing]** Upgrade opentelemetry dependencies ([#10472](https://github.com/traefik/traefik/pull/10472) by [mmatur](https://github.com/mmatur)) +- **[middleware,authentication,tracing]** Add captured headers options for tracing ([#10457](https://github.com/traefik/traefik/pull/10457) by [rtribotte](https://github.com/rtribotte)) +- **[middleware,metrics]** Semconv OTLP stable HTTP metrics ([#10421](https://github.com/traefik/traefik/pull/10421) by [mmatur](https://github.com/mmatur)) +- **[plugins]** Upgrade http-wasm host to v0.6.0 to support clients using v0.4.0 ([#10475](https://github.com/traefik/traefik/pull/10475) by [jcchavezs](https://github.com/jcchavezs)) +- **[tracing]** Support OTEL_PROPAGATORS to configure tracing propagation ([#10465](https://github.com/traefik/traefik/pull/10465) by [youkoulayley](https://github.com/youkoulayley)) + +**Bug fixes:** +- Fix a regression on flags using spaces between key and value ([#10445](https://github.com/traefik/traefik/pull/10445) by [ldez](https://github.com/ldez)) + +**Documentation:** +- **[k8s,k8s/gatewayapi]** Add ReferenceGrants to Gateway API Traefik controller RBAC ([#10462](https://github.com/traefik/traefik/pull/10462) by [rtribotte](https://github.com/rtribotte)) +- **[k8s]** Fix invalid version in docs about Gateway API on Traefik v3 ([#10474](https://github.com/traefik/traefik/pull/10474) by [mloiseleur](https://github.com/mloiseleur)) +- **[rules]** Improve ruleSyntax option documentation ([#10441](https://github.com/traefik/traefik/pull/10441) by [rtribotte](https://github.com/rtribotte)) +- Fix typo in migration docs ([#10478](https://github.com/traefik/traefik/pull/10478) by [Eisberge](https://github.com/Eisberge)) + +**Misc:** +- Merge v2.11 into v3.0 ([#10513](https://github.com/traefik/traefik/pull/10513) by [mmatur](https://github.com/mmatur)) + ## [v3.0.0-rc1](https://github.com/traefik/traefik/tree/v3.0.0-rc1) (2024-02-13) [All Commits](https://github.com/traefik/traefik/compare/v3.0.0-beta5...v3.0.0-rc1) diff --git a/Makefile b/Makefile index 5c9b15656..c2be95619 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/nul REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]') BIN_NAME := traefik -CODENAME := cheddar +CODENAME ?= cheddar DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p') diff --git a/cmd/internal/gen/centrifuge.go b/cmd/internal/gen/centrifuge.go index df5d46fd8..2c0f6e634 100644 --- a/cmd/internal/gen/centrifuge.go +++ b/cmd/internal/gen/centrifuge.go @@ -160,7 +160,7 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string, b := strings.Builder{} b.WriteString(fmt.Sprintf("type %s struct {\n", name)) - for i := 0; i < obj.NumFields(); i++ { + for i := range obj.NumFields() { field := obj.Field(i) if !field.Exported() { diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 821f391ed..999e56308 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -46,7 +46,6 @@ import ( "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" - "go.opentelemetry.io/otel/trace" ) func main() { @@ -196,10 +195,17 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err // Observability metricRegistries := registerMetricClients(staticConfiguration.Metrics) + var semConvMetricRegistry *metrics.SemConvMetricsRegistry + if staticConfiguration.Metrics != nil && staticConfiguration.Metrics.OTLP != nil { + semConvMetricRegistry, err = metrics.NewSemConvMetricRegistry(ctx, staticConfiguration.Metrics.OTLP) + if err != nil { + return nil, fmt.Errorf("unable to create SemConv metric registry: %w", err) + } + } metricsRegistry := metrics.NewMultiRegistry(metricRegistries) accessLog := setupAccessLog(staticConfiguration.AccessLog) tracer, tracerCloser := setupTracing(staticConfiguration.Tracing) - observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, accessLog, tracer, tracerCloser) + observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, semConvMetricRegistry, accessLog, tracer, tracerCloser) // Entrypoints @@ -563,7 +569,7 @@ func setupAccessLog(conf *types.AccessLog) *accesslog.Handler { return accessLoggerMiddleware } -func setupTracing(conf *static.Tracing) (trace.Tracer, io.Closer) { +func setupTracing(conf *static.Tracing) (*tracing.Tracer, io.Closer) { if conf == nil { return nil, nil } diff --git a/docs/content/deprecation/releases.md b/docs/content/deprecation/releases.md index 1e73eda6d..3cfe9ddff 100644 --- a/docs/content/deprecation/releases.md +++ b/docs/content/deprecation/releases.md @@ -6,7 +6,8 @@ Below is a non-exhaustive list of versions and their maintenance status: | Version | Release Date | Active Support | Security Support | |---------|--------------|--------------------|------------------| -| 2.10 | Apr 24, 2023 | Yes | Yes | +| 2.11 | Feb 12, 2024 | Yes | Yes | +| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 | No | | 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 | No | | 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No | | 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No | diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index f423b23db..99e64c76d 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -294,6 +294,12 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni LEGO_DISABLE_CNAME_SUPPORT=true ``` +!!! warning "Multiple DNS Challenge provider" + + Multiple DNS challenge provider are not supported with Traefik, but you can use `CNAME` to handle that. + For example, if you have `example.org` (account foo) and `example.com` (account bar) you can create a CNAME on `example.org` called `_acme-challenge.example.org` pointing to `challenge.example.com`. + This way, you can obtain certificates for `example.com` with the `foo` account. + !!! important A `provider` is mandatory. @@ -331,6 +337,7 @@ For complete details, refer to your provider's _Additional configuration_ link. | [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) | | [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) | | [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) | +| [CPanel and WHM](https://cpanel.net/) | `cpanel` | `CPANEL_MODE`, `CPANEL_USERNAME`, `CPANEL_TOKEN`, `CPANEL_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/cpanel) | | [Derak Cloud](https://derak.cloud/) | `derak` | `DERAK_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/derak) | | [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) | | [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) | @@ -352,7 +359,7 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) | | [Freemyip.com](https://freemyip.com) | `freemyip` | `FREEMYIP_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/freemyip) | | [G-Core](https://gcore.com/dns/) | `gcore` | `GCORE_PERMANENT_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gcore) | -| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) | +| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_PERSONAL_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) | | [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) | | [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) | | [GoDaddy](https://www.godaddy.com) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) | @@ -381,6 +388,7 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) | | [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) | | [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) | +| [Mail-in-a-Box](https://mailinabox.email) | `mailinabox` | `MAILINABOX_EMAIL`, `MAILINABOX_PASSWORD`, `MAILINABOX_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/mailinabox) | | [Metaname](https://metaname.net) | `metaname` | `METANAME_ACCOUNT_REFERENCE`, `METANAME_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/metaname) | | [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) | | [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) | @@ -412,6 +420,7 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) | | [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) | | [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) | +| [Shellrent](https://www.shellrent.com) | `shellrent` | `SHELLRENT_USERNAME`, `SHELLRENT_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/shellrent) | | [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) | | [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) | | [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) | diff --git a/docs/content/observability/metrics/overview.md b/docs/content/observability/metrics/overview.md index f968e77e6..082d4aed0 100644 --- a/docs/content/observability/metrics/overview.md +++ b/docs/content/observability/metrics/overview.md @@ -12,7 +12,8 @@ Traefik supports these metrics backends: - [Prometheus](./prometheus.md) - [StatsD](./statsd.md) -Traefik Proxy hosts an official Grafana dashboard for both [on-premises](https://grafana.com/grafana/dashboards/17346) and [Kubernetes](https://grafana.com/grafana/dashboards/17347) deployments. +Traefik Proxy hosts an official Grafana dashboard for both [on-premises](https://grafana.com/grafana/dashboards/17346) +and [Kubernetes](https://grafana.com/grafana/dashboards/17347) deployments. ## Common Options @@ -29,7 +30,7 @@ metrics: ```toml tab="File (TOML)" [metrics] - addInternals = true +addInternals = true ``` ```bash tab="CLI" @@ -85,10 +86,10 @@ traefik_tls_certs_not_after Here is a comprehensive list of labels that are provided by the global metrics: -| Label | Description | example | -|---------------|----------------------------------------|----------------------| -| `entrypoint` | Entrypoint that handled the connection | "example_entrypoint" | -| `protocol` | Connection protocol | "TCP" | +| Label | Description | example | +|--------------|----------------------------------------|----------------------| +| `entrypoint` | Entrypoint that handled the connection | "example_entrypoint" | +| `protocol` | Connection protocol | "TCP" | ## HTTP Metrics @@ -281,3 +282,49 @@ Here is a comprehensive list of labels that are provided by the metrics: If the HTTP method verb on a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`), then the value for the method label becomes `EXTENSION_METHOD`. + +## Semantic Conventions for HTTP Metrics + +Traefik Proxy follows [official OTLP semantic conventions v1.23.1](https://github.com/open-telemetry/semantic-conventions/blob/v1.23.1/docs/http/http-metrics.md). + +### HTTP Server + +| Metric | Type | [Labels](#labels) | Description | +|-------------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| http.server.request.duration | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP server requests | + +#### Labels + +Here is a comprehensive list of labels that are provided by the metrics: + +| Label | Description | example | +|-----------------------------|--------------------------------------------------------------|---------------| +| `error.type` | Describes a class of error the operation ended with | "500" | +| `http.request.method` | HTTP request method | "GET" | +| `http.response.status_code` | HTTP response status code | "200" | +| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | +| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | +| `server.address` | Name of the local HTTP server that received the request | "example.com" | +| `server.port` | Port of the local HTTP server that received the request | "80" | +| `url.scheme` | The URI scheme component identifying the used protocol | "http" | + +### HTTP Client + +| Metric | Type | [Labels](#labels) | Description | +|-------------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| http.client.request.duration | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP client requests | + +#### Labels + +Here is a comprehensive list of labels that are provided by the metrics: + +| Label | Description | example | +|-----------------------------|--------------------------------------------------------------|---------------| +| `error.type` | Describes a class of error the operation ended with | "500" | +| `http.request.method` | HTTP request method | "GET" | +| `http.response.status_code` | HTTP response status code | "200" | +| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | +| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | +| `server.address` | Name of the local HTTP server that received the request | "example.com" | +| `server.port` | Port of the local HTTP server that received the request | "80" | +| `url.scheme` | The URI scheme component identifying the used protocol | "http" | diff --git a/docs/content/observability/metrics/prometheus.md b/docs/content/observability/metrics/prometheus.md index 4a3ec0a32..9c610332c 100644 --- a/docs/content/observability/metrics/prometheus.md +++ b/docs/content/observability/metrics/prometheus.md @@ -235,4 +235,4 @@ traefik_entrypoint_requests_total{code="200",entrypoint="web",method="GET",proto // For incoming requests, the Host header is promoted to the // Request.Host field and removed from the Header map. - As a workaround, to obtain the Host of a request as a label, one should use instead the `X-Forwarded-For` header. + As a workaround, to obtain the Host of a request as a label, one should use instead the `X-Forwarded-Host` header. diff --git a/docs/content/observability/metrics/statsd.md b/docs/content/observability/metrics/statsd.md index 79472dd06..042e937cd 100644 --- a/docs/content/observability/metrics/statsd.md +++ b/docs/content/observability/metrics/statsd.md @@ -69,7 +69,7 @@ metrics: _Optional, Default=false_ -Enable metrics on entry points. +Enable metrics on routers. ```yaml tab="File (YAML)" metrics: diff --git a/docs/content/observability/tracing/overview.md b/docs/content/observability/tracing/overview.md index 901377150..72dd20cd6 100644 --- a/docs/content/observability/tracing/overview.md +++ b/docs/content/observability/tracing/overview.md @@ -116,3 +116,47 @@ tracing: --tracing.globalAttributes.attr1=foo --tracing.globalAttributes.attr2=bar ``` + +#### `capturedRequestHeaders` + +_Optional, Default=empty_ + +Defines the list of request headers to add as attributes. +It applies to client and server kind spans. + +```yaml tab="File (YAML)" +tracing: + capturedRequestHeaders: + - X-CustomHeader +``` + +```toml tab="File (TOML)" +[tracing] + capturedRequestHeaders = ["X-CustomHeader"] +``` + +```bash tab="CLI" +--tracing.capturedRequestHeaders[0]=X-CustomHeader +``` + +#### `capturedResponseHeaders` + +_Optional, Default=empty_ + +Defines the list of response headers to add as attributes. +It applies to client and server kind spans. + +```yaml tab="File (YAML)" +tracing: + capturedResponseHeaders: + - X-CustomHeader +``` + +```toml tab="File (TOML)" +[tracing] + capturedResponseHeaders = ["X-CustomHeader"] +``` + +```bash tab="CLI" +--tracing.capturedResponseHeaders[0]=X-CustomHeader +``` \ No newline at end of file diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 312c1ad26..b3e9f852b 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -192,9 +192,9 @@ See the [Docker API Access](#docker-api-access) section for more information. ??? example "Using SSH" - Using Docker 18.09+ you can connect Traefik to daemon using SSH + Using Docker 18.09+ you can connect Traefik to daemon using SSH. We specify the SSH host and user in Traefik's configuration file. - Note that is server requires public keys for authentication you must have those accessible for user who runs Traefik. + Note that if the server requires public keys for authentication, you must have them accessible for the user running Traefik. ```yaml tab="File (YAML)" providers: diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 7f3fec5ad..5c21cab3d 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -225,6 +225,12 @@ - "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42" - "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar" - "traefik.tcp.services.tcpservice01.loadbalancer.server.tls=true" +- "traefik.tls.stores.store0.defaultgeneratedcert.domain.main=foobar" +- "traefik.tls.stores.store0.defaultgeneratedcert.domain.sans=foobar, foobar" +- "traefik.tls.stores.store0.defaultgeneratedcert.resolver=foobar" +- "traefik.tls.stores.store1.defaultgeneratedcert.domain.main=foobar" +- "traefik.tls.stores.store1.defaultgeneratedcert.domain.sans=foobar, foobar" +- "traefik.tls.stores.store1.defaultgeneratedcert.resolver=foobar" - "traefik.udp.routers.udprouter0.entrypoints=foobar, foobar" - "traefik.udp.routers.udprouter0.service=foobar" - "traefik.udp.routers.udprouter1.entrypoints=foobar, foobar" diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index d78150a72..45cd217a5 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -139,7 +139,7 @@ Default middlewares for the routers linked to the entry point. Applies a permanent redirection. (Default: ```true```) `--entrypoints..http.redirections.entrypoint.priority`: -Priority of the generated router. (Default: ```2147483646```) +Priority of the generated router. (Default: ```9223372036854775806```) `--entrypoints..http.redirections.entrypoint.scheme`: Scheme used for the redirection. (Default: ```https```) @@ -337,7 +337,7 @@ Enable metrics on routers. (Default: ```false```) Enable metrics on services. (Default: ```true```) `--metrics.otlp.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```) +Boundaries for latency metrics. (Default: ```0.005000, 0.010000, 0.025000, 0.050000, 0.075000, 0.100000, 0.250000, 0.500000, 0.750000, 1.000000, 2.500000, 5.000000, 7.500000, 10.000000```) `--metrics.otlp.grpc.endpoint`: Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```) @@ -1017,6 +1017,12 @@ OpenTracing configuration. (Default: ```false```) `--tracing.addinternals`: Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```) +`--tracing.capturedrequestheaders`: +Request headers to add as attributes for server and client spans. + +`--tracing.capturedresponseheaders`: +Response headers to add as attributes for server and client spans. + `--tracing.globalattributes.`: Defines additional attributes (key:value) on all spans. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 7947b9ddd..f7ab1bd79 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -148,7 +148,7 @@ Default middlewares for the routers linked to the entry point. Applies a permanent redirection. (Default: ```true```) `TRAEFIK_ENTRYPOINTS__HTTP_REDIRECTIONS_ENTRYPOINT_PRIORITY`: -Priority of the generated router. (Default: ```2147483646```) +Priority of the generated router. (Default: ```9223372036854775806```) `TRAEFIK_ENTRYPOINTS__HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME`: Scheme used for the redirection. (Default: ```https```) @@ -337,7 +337,7 @@ Enable metrics on routers. (Default: ```false```) Enable metrics on services. (Default: ```true```) `TRAEFIK_METRICS_OTLP_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```) +Boundaries for latency metrics. (Default: ```0.005000, 0.010000, 0.025000, 0.050000, 0.075000, 0.100000, 0.250000, 0.500000, 0.750000, 1.000000, 2.500000, 5.000000, 7.500000, 10.000000```) `TRAEFIK_METRICS_OTLP_GRPC_ENDPOINT`: Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```) @@ -1017,6 +1017,12 @@ OpenTracing configuration. (Default: ```false```) `TRAEFIK_TRACING_ADDINTERNALS`: Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```) +`TRAEFIK_TRACING_CAPTUREDREQUESTHEADERS`: +Request headers to add as attributes for server and client spans. + +`TRAEFIK_TRACING_CAPTUREDRESPONSEHEADERS`: +Response headers to add as attributes for server and client spans. + `TRAEFIK_TRACING_GLOBALATTRIBUTES_`: Defines additional attributes (key:value) on all spans. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 504d7c9b6..cef7d56e9 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -380,6 +380,8 @@ [tracing] serviceName = "foobar" + capturedRequestHeaders = ["foobar", "foobar"] + capturedResponseHeaders = ["foobar", "foobar"] sampleRate = 42.0 addInternals = true [tracing.globalAttributes] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index ffb9d75d8..84404356d 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -418,6 +418,12 @@ tracing: globalAttributes: name0: foobar name1: foobar + capturedRequestHeaders: + - foobar + - foobar + capturedResponseHeaders: + - foobar + - foobar sampleRate: 42 addInternals: true otlp: diff --git a/docs/content/routing/entrypoints.md b/docs/content/routing/entrypoints.md index 75376bd9b..67af1e9a2 100644 --- a/docs/content/routing/entrypoints.md +++ b/docs/content/routing/entrypoints.md @@ -702,29 +702,29 @@ _Optional, Default=0_ The maximum number of requests Traefik can handle before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY). Zero means no limit. - ```yaml tab="File (YAML)" - ## Static configuration - entryPoints: - name: - address: ":8888" - transport: - keepAliveMaxRequests: 42 - ``` +```yaml tab="File (YAML)" +## Static configuration +entryPoints: + name: + address: ":8888" + transport: + keepAliveMaxRequests: 42 +``` - ```toml tab="File (TOML)" - ## Static configuration - [entryPoints] - [entryPoints.name] - address = ":8888" - [entryPoints.name.transport] - keepAliveMaxRequests = 42 - ``` +```toml tab="File (TOML)" +## Static configuration +[entryPoints] + [entryPoints.name] + address = ":8888" + [entryPoints.name.transport] + keepAliveMaxRequests = 42 +``` - ```bash tab="CLI" - ## Static configuration - --entryPoints.name.address=:8888 - --entryPoints.name.transport.keepAliveMaxRequests=42 - ``` +```bash tab="CLI" +## Static configuration +--entryPoints.name.address=:8888 +--entryPoints.name.transport.keepAliveMaxRequests=42 +``` #### `keepAliveMaxTime` @@ -732,29 +732,29 @@ _Optional, Default=0s_ The maximum duration Traefik can handle requests before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY). Zero means no limit. - ```yaml tab="File (YAML)" - ## Static configuration - entryPoints: - name: - address: ":8888" - transport: - keepAliveMaxTime: 42s - ``` +```yaml tab="File (YAML)" +## Static configuration +entryPoints: + name: + address: ":8888" + transport: + keepAliveMaxTime: 42s +``` - ```toml tab="File (TOML)" - ## Static configuration - [entryPoints] - [entryPoints.name] - address = ":8888" - [entryPoints.name.transport] - keepAliveMaxTime = 42s - ``` +```toml tab="File (TOML)" +## Static configuration +[entryPoints] + [entryPoints.name] + address = ":8888" + [entryPoints.name.transport] + keepAliveMaxTime = 42s +``` - ```bash tab="CLI" - ## Static configuration - --entryPoints.name.address=:8888 - --entryPoints.name.transport.keepAliveMaxTime=42s - ``` +```bash tab="CLI" +## Static configuration +--entryPoints.name.address=:8888 +--entryPoints.name.transport.keepAliveMaxTime=42s +``` ### ProxyProtocol @@ -972,7 +972,7 @@ This section is a convenience to enable (permanent) redirecting of all incoming ??? info "`entryPoint.priority`" - _Optional, Default=MaxInt32-1 (2147483646)_ + _Optional, Default=MaxInt-1_ Priority of the generated router. diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 57dd2466a..dad91d472 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -784,7 +784,7 @@ spec: #### `peerCertURI` -_Optional, Default=false_ +_Optional, Default=""_ `peerCertURI` defines the URI used to match against SAN URIs during the server's certificate verification. diff --git a/go.mod b/go.mod index 690101445..513d968c9 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/fatih/structs v1.1.0 github.com/fsnotify/fsnotify v1.7.0 - github.com/go-acme/lego/v4 v4.15.0 + github.com/go-acme/lego/v4 v4.16.1 github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea github.com/golang/protobuf v1.5.3 github.com/google/go-github/v28 v28.1.1 @@ -172,7 +172,7 @@ require ( github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-jose/go-jose/v3 v3.0.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -183,6 +183,7 @@ require ( github.com/go-playground/validator/v10 v10.15.1 // indirect github.com/go-resty/resty/v2 v2.11.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect @@ -255,6 +256,7 @@ require ( github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/freemyip v0.2.0 // indirect github.com/nrdcg/goinwx v0.10.0 // indirect + github.com/nrdcg/mailinabox v0.2.0 // indirect github.com/nrdcg/namesilo v0.2.1 // indirect github.com/nrdcg/nodion v0.1.0 // indirect github.com/nrdcg/porkbun v0.3.0 // indirect @@ -263,7 +265,7 @@ require ( github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect - github.com/opencontainers/runc v1.1.5 // indirect + github.com/opencontainers/runc v1.1.7 // indirect github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect github.com/ovh/go-ovh v1.4.3 // indirect github.com/pelletier/go-toml/v2 v2.0.9 // indirect @@ -288,7 +290,7 @@ require ( github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/softlayer/softlayer-go v1.1.3 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect @@ -320,9 +322,9 @@ require ( go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.18.0 // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/term v0.17.0 // indirect google.golang.org/api v0.149.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect diff --git a/go.sum b/go.sum index 8bcf31516..1ff19c374 100644 --- a/go.sum +++ b/go.sum @@ -179,7 +179,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -189,7 +188,6 @@ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/civo/civogo v0.3.11 h1:mON/fyrV946Sbk6paRtOSGsN+asCgCmHCgArf5xmGxM= @@ -208,7 +206,6 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -231,7 +228,6 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -248,7 +244,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -278,7 +273,6 @@ github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -317,7 +311,8 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -335,15 +330,15 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-acme/lego/v4 v4.15.0 h1:A7MHEU3b+TDFqhC/HmzMJnzPbyeaYvMZQBbqgvbThhU= -github.com/go-acme/lego/v4 v4.15.0/go.mod h1:eeGhjW4zWT7Ccqa3sY7ayEqFLCAICx+mXgkMHKIkLxg= +github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ= +github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE= 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= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea h1:CnEQOUv4ilElSwFB9g/lVmz206oLE4aNZDYngIY1Gvg= @@ -395,6 +390,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= @@ -412,7 +409,6 @@ github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -796,7 +792,6 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -810,7 +805,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 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= @@ -841,6 +835,8 @@ github.com/nrdcg/freemyip v0.2.0 h1:/GscavT4GVqAY13HExl5UyoB4wlchv6Cg5NYDGsUoJ8= github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4= github.com/nrdcg/goinwx v0.10.0 h1:6W630bjDxQD6OuXKqrFRYVpTt0G/9GXXm3CeOrN0zJM= github.com/nrdcg/goinwx v0.10.0/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4= +github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk= +github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc= github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw= @@ -879,10 +875,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= +github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1001,7 +995,6 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22 h1:wJrcTdddKOI8TFxs8cemnhKP2E github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= @@ -1017,7 +1010,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1038,8 +1030,9 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -1077,7 +1070,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g= github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2 h1:xwMw7LFhV9dbvot9A7NLClP9udqbjrQlIwWMH8e7uiQ= github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2/go.mod h1:hL4gB6APAasMR2NNi/JHzqKkxW3EPQlFgLEq9PMi2t0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8= @@ -1134,8 +1126,6 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c h1:Qt/YKpE8uAKNF4x2mwBZxmVo2WtgUL1WFDeXr1nlfpA= github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c/go.mod h1:A2voDnpONyqdplUDK0lt5y4XHLiBXPBw7iQES8+ZWRw= github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50= @@ -1240,7 +1230,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1258,8 +1247,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 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= @@ -1319,7 +1308,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -1372,7 +1360,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1385,7 +1372,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1412,11 +1398,8 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc 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-20210906170528-6f6e22806c34/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-20211025201205-69cdffdb9359/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-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1444,8 +1427,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/integration/conf_throttling_test.go b/integration/conf_throttling_test.go index 297bb56fd..19770bb9e 100644 --- a/integration/conf_throttling_test.go +++ b/integration/conf_throttling_test.go @@ -34,11 +34,11 @@ func (s *ThrottlingSuite) TestThrottleConfReload() { s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml")) // wait for Traefik - err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal")) + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.BodyContains("rest@internal")) require.NoError(s.T(), err) // Expected a 404 as we did not configure anything. - err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) + err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound)) require.NoError(s.T(), err) config := &dynamic.Configuration{ @@ -67,7 +67,7 @@ func (s *ThrottlingSuite) TestThrottleConfReload() { confChanges := 10 - for i := 0; i < confChanges; i++ { + for i := range confChanges { config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router data, err := json.Marshal(config) require.NoError(s.T(), err) diff --git a/integration/fixtures/tracing/simple-opentelemetry.toml b/integration/fixtures/tracing/simple-opentelemetry.toml index 0599bb18d..77cb436bb 100644 --- a/integration/fixtures/tracing/simple-opentelemetry.toml +++ b/integration/fixtures/tracing/simple-opentelemetry.toml @@ -15,6 +15,17 @@ [entryPoints.web] address = ":8000" +# Adding metrics to confirm that there is no wrong interaction with tracing. +[metrics] +{{if .IsHTTP}} + [metrics.otlp.http] + endpoint = "http://{{.IP}}:4318" +{{else}} + [metrics.otlp.grpc] + endpoint = "{{.IP}}:4317" + insecure = true +{{end}} + [tracing] servicename = "tracing" sampleRate = 1.0 diff --git a/integration/healthcheck_test.go b/integration/healthcheck_test.go index 1cfabb176..1276f09dd 100644 --- a/integration/healthcheck_test.go +++ b/integration/healthcheck_test.go @@ -312,7 +312,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify load-balancing on root still works, and that we're getting an alternation between wsp2, and wsp4. reachedServers := make(map[string]int) - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(rootReq) require.NoError(s.T(), err) @@ -352,7 +352,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify load-balancing on foo still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc. want := `IP: ` + s.whoami2IP - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(fooReq) require.NoError(s.T(), err) @@ -368,7 +368,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify load-balancing on bar still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc. want = `IP: ` + s.whoami2IP - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(barReq) require.NoError(s.T(), err) @@ -390,7 +390,7 @@ func (s *HealthCheckSuite) TestPropagate() { try.Sleep(time.Second) // Verify that everything is down, and that we get 503s everywhere. - for i := 0; i < 2; i++ { + for range 2 { resp, err := client.Do(rootReq) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode) @@ -417,7 +417,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify everything is up on root router. reachedServers = make(map[string]int) - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(rootReq) require.NoError(s.T(), err) @@ -452,7 +452,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify everything is up on foo router. reachedServers = make(map[string]int) - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(fooReq) require.NoError(s.T(), err) @@ -487,7 +487,7 @@ func (s *HealthCheckSuite) TestPropagate() { // Verify everything is up on bar router. reachedServers = make(map[string]int) - for i := 0; i < 4; i++ { + for range 4 { resp, err := client.Do(barReq) require.NoError(s.T(), err) diff --git a/integration/integration_test.go b/integration/integration_test.go index 51df6870c..f46b15209 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -243,7 +243,7 @@ func (s *BaseSuite) createComposeProject(name string) { } if containerConfig.Deploy.Replicas > 0 { - for i := 0; i < containerConfig.Deploy.Replicas; i++ { + for i := range containerConfig.Deploy.Replicas { id = fmt.Sprintf("%s-%d", id, i+1) con, err := s.createContainer(ctx, containerConfig, id, mounts) require.NoError(s.T(), err) diff --git a/integration/simple_test.go b/integration/simple_test.go index 8e2f14ec1..c8ebb8d6c 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -311,7 +311,7 @@ func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService() { require.NoError(s.T(), err) // adding a loop to test if metrics are not deleted - for i := 0; i < 10; i++ { + for range 10 { request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil) require.NoError(s.T(), err) @@ -876,7 +876,7 @@ func (s *SimpleSuite) TestWRR() { require.NoError(s.T(), err) repartition := map[string]int{} - for i := 0; i < 4; i++ { + for range 4 { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) require.NoError(s.T(), err) @@ -922,7 +922,7 @@ func (s *SimpleSuite) TestWRRSticky() { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) require.NoError(s.T(), err) - for i := 0; i < 4; i++ { + for range 4 { response, err := http.DefaultClient.Do(req) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusOK, response.StatusCode) @@ -978,7 +978,7 @@ func (s *SimpleSuite) TestMirror() { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) require.NoError(s.T(), err) - for i := 0; i < 10; i++ { + for range 10 { response, err := http.DefaultClient.Do(req) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusOK, response.StatusCode) @@ -1049,7 +1049,7 @@ func (s *SimpleSuite) TestMirrorWithBody() { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", bytes.NewBuffer(body20)) require.NoError(s.T(), err) req.Header.Set("Size", "20") - for i := 0; i < 10; i++ { + for range 10 { response, err := http.DefaultClient.Do(req) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusOK, response.StatusCode) @@ -1070,7 +1070,7 @@ func (s *SimpleSuite) TestMirrorWithBody() { req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoamiWithMaxBody", bytes.NewBuffer(body5)) require.NoError(s.T(), err) req.Header.Set("Size", "5") - for i := 0; i < 10; i++ { + for range 10 { response, err := http.DefaultClient.Do(req) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusOK, response.StatusCode) @@ -1091,7 +1091,7 @@ func (s *SimpleSuite) TestMirrorWithBody() { req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoamiWithMaxBody", bytes.NewBuffer(body20)) require.NoError(s.T(), err) req.Header.Set("Size", "20") - for i := 0; i < 10; i++ { + for range 10 { response, err := http.DefaultClient.Do(req) require.NoError(s.T(), err) assert.Equal(s.T(), http.StatusOK, response.StatusCode) @@ -1137,7 +1137,7 @@ func (s *SimpleSuite) TestMirrorCanceled() { err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("mirror1", "mirror2", "service1")) require.NoError(s.T(), err) - for i := 0; i < 5; i++ { + for range 5 { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) require.NoError(s.T(), err) diff --git a/integration/tcp_test.go b/integration/tcp_test.go index 059714d92..d46d91477 100644 --- a/integration/tcp_test.go +++ b/integration/tcp_test.go @@ -287,7 +287,7 @@ func (s *TCPSuite) TestWRR() { require.NoError(s.T(), err) call := map[string]int{} - for i := 0; i < 4; i++ { + for range 4 { // Traefik passes through, termination handled by whoami-b or whoami-bb out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test") require.NoError(s.T(), err) diff --git a/integration/testdata/rawdata-consul.json b/integration/testdata/rawdata-consul.json index a2e99f1bc..0888c3af9 100644 --- a/integration/testdata/rawdata-consul.json +++ b/integration/testdata/rawdata-consul.json @@ -53,7 +53,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -69,7 +69,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-etcd.json b/integration/testdata/rawdata-etcd.json index 8564dd454..18ed673e3 100644 --- a/integration/testdata/rawdata-etcd.json +++ b/integration/testdata/rawdata-etcd.json @@ -53,7 +53,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -69,7 +69,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-gateway.json b/integration/testdata/rawdata-gateway.json index 909ce1326..c47960257 100644 --- a/integration/testdata/rawdata-gateway.json +++ b/integration/testdata/rawdata-gateway.json @@ -6,7 +6,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -22,7 +22,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-ingress-label-selector.json b/integration/testdata/rawdata-ingress-label-selector.json index f8e4c88a3..703f6dd95 100644 --- a/integration/testdata/rawdata-ingress-label-selector.json +++ b/integration/testdata/rawdata-ingress-label-selector.json @@ -6,7 +6,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -22,7 +22,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-ingress.json b/integration/testdata/rawdata-ingress.json index 305cb3015..7cd63e333 100644 --- a/integration/testdata/rawdata-ingress.json +++ b/integration/testdata/rawdata-ingress.json @@ -6,7 +6,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -22,7 +22,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-ingressclass-disabled.json b/integration/testdata/rawdata-ingressclass-disabled.json index 14649419c..458fc2e1b 100644 --- a/integration/testdata/rawdata-ingressclass-disabled.json +++ b/integration/testdata/rawdata-ingressclass-disabled.json @@ -6,7 +6,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -22,7 +22,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" @@ -71,4 +71,4 @@ "status": "enabled" } } -} \ No newline at end of file +} diff --git a/integration/testdata/rawdata-ingressclass.json b/integration/testdata/rawdata-ingressclass.json index a020096cc..346396d6c 100644 --- a/integration/testdata/rawdata-ingressclass.json +++ b/integration/testdata/rawdata-ingressclass.json @@ -6,7 +6,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -22,7 +22,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-redis.json b/integration/testdata/rawdata-redis.json index 6f0782380..182276fcb 100644 --- a/integration/testdata/rawdata-redis.json +++ b/integration/testdata/rawdata-redis.json @@ -53,7 +53,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -69,7 +69,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/testdata/rawdata-zk.json b/integration/testdata/rawdata-zk.json index 5722bb2ce..7381d8c27 100644 --- a/integration/testdata/rawdata-zk.json +++ b/integration/testdata/rawdata-zk.json @@ -53,7 +53,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646, + "priority": 9223372036854775806, "status": "enabled", "using": [ "traefik" @@ -69,7 +69,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645, + "priority": 9223372036854775805, "status": "enabled", "using": [ "traefik" diff --git a/integration/tracing_test.go b/integration/tracing_test.go index ef92015bd..aa5500108 100644 --- a/integration/tracing_test.go +++ b/integration/tracing_test.go @@ -302,7 +302,7 @@ func (s *TracingSuite) TestOpentelemetryRetry() { s.traefikCmd(withConfigFile(file)) // wait for traefik - err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth")) + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("basic-auth")) require.NoError(s.T(), err) err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway)) @@ -425,7 +425,7 @@ func (s *TracingSuite) TestNoInternals() { s.traefikCmd(withConfigFile(file)) // wait for traefik - err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth")) + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("basic-auth")) require.NoError(s.T(), err) err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) diff --git a/integration/udp_test.go b/integration/udp_test.go index e8acadfba..b8a921f32 100644 --- a/integration/udp_test.go +++ b/integration/udp_test.go @@ -82,7 +82,7 @@ func (s *UDPSuite) TestWRR() { stop := make(chan struct{}) go func() { call := map[string]int{} - for i := 0; i < 8; i++ { + for range 8 { out, err := guessWhoUDP("127.0.0.1:8093") require.NoError(s.T(), err) switch { diff --git a/internal/gendoc.go b/internal/gendoc.go index 93c03366e..deb7757ee 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -206,7 +206,7 @@ func clean(element any) { valueSvcRoot := valSvcs.MapIndex(key).Elem() var svcFieldNames []string - for i := 0; i < valueSvcRoot.NumField(); i++ { + for i := range valueSvcRoot.NumField() { svcFieldNames = append(svcFieldNames, valueSvcRoot.Type().Field(i).Name) } diff --git a/pkg/api/criterion.go b/pkg/api/criterion.go index 2e3d2f7e6..7f9ba3e02 100644 --- a/pkg/api/criterion.go +++ b/pkg/api/criterion.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "net/url" + "slices" "strconv" "strings" ) @@ -59,13 +60,9 @@ func (c *searchCriterion) searchIn(values ...string) bool { return true } - for _, v := range values { - if strings.Contains(strings.ToLower(v), strings.ToLower(c.Search)) { - return true - } - } - - return false + return slices.ContainsFunc(values, func(v string) bool { + return strings.Contains(strings.ToLower(v), strings.ToLower(c.Search)) + }) } func (c *searchCriterion) filterService(name string) bool { diff --git a/pkg/api/handler.go b/pkg/api/handler.go index a8345b9ff..b137e5401 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -149,7 +149,7 @@ func getProviderName(id string) string { func extractType(element interface{}) string { v := reflect.ValueOf(element).Elem() - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { field := v.Field(i) if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) { diff --git a/pkg/api/handler_entrypoint_test.go b/pkg/api/handler_entrypoint_test.go index 1630f2f7c..18bf5cc4e 100644 --- a/pkg/api/handler_entrypoint_test.go +++ b/pkg/api/handler_entrypoint_test.go @@ -256,7 +256,7 @@ func TestHandler_EntryPoints(t *testing.T) { func generateEntryPoints(nb int) map[string]*static.EntryPoint { eps := make(map[string]*static.EntryPoint, nb) - for i := 0; i < nb; i++ { + for i := range nb { eps[fmt.Sprintf("ep%2d", i)] = &static.EntryPoint{ Address: ":" + strconv.Itoa(i), } diff --git a/pkg/api/handler_http_test.go b/pkg/api/handler_http_test.go index e76628261..5d5f6d25e 100644 --- a/pkg/api/handler_http_test.go +++ b/pkg/api/handler_http_test.go @@ -1050,7 +1050,7 @@ func TestHandler_HTTP(t *testing.T) { func generateHTTPRouters(nbRouters int) map[string]*runtime.RouterInfo { routers := make(map[string]*runtime.RouterInfo, nbRouters) - for i := 0; i < nbRouters; i++ { + for i := range nbRouters { routers[fmt.Sprintf("bar%2d@myprovider", i)] = &runtime.RouterInfo{ Router: &dynamic.Router{ EntryPoints: []string{"web"}, diff --git a/pkg/api/handler_overview.go b/pkg/api/handler_overview.go index 240ceab0b..7b15d513e 100644 --- a/pkg/api/handler_overview.go +++ b/pkg/api/handler_overview.go @@ -226,7 +226,7 @@ func getProviders(conf static.Configuration) []string { var providers []string v := reflect.ValueOf(conf.Providers).Elem() - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { field := v.Field(i) if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct { if !field.IsNil() { @@ -256,7 +256,7 @@ func getMetrics(conf static.Configuration) string { } v := reflect.ValueOf(conf.Metrics).Elem() - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { field := v.Field(i) if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct { if !field.IsNil() { @@ -274,7 +274,7 @@ func getTracing(conf static.Configuration) string { } v := reflect.ValueOf(conf.Tracing).Elem() - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { field := v.Field(i) if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct { if !field.IsNil() { diff --git a/pkg/collector/hydratation/hydration.go b/pkg/collector/hydratation/hydration.go index a5ec52dee..c45cfcfbb 100644 --- a/pkg/collector/hydratation/hydration.go +++ b/pkg/collector/hydratation/hydration.go @@ -56,7 +56,7 @@ func fill(field reflect.Value) error { case reflect.Int64: switch field.Type() { case reflect.TypeOf(types.Duration(time.Second)): - setTyped(field, int64(defaultNumber*int(time.Second))) + setTyped(field, types.Duration(defaultNumber*time.Second)) default: setTyped(field, int64(defaultNumber)) } @@ -93,12 +93,12 @@ func setTyped(field reflect.Value, i interface{}) { func setMap(field reflect.Value) error { field.Set(reflect.MakeMap(field.Type())) - for i := 0; i < mapItemNumber; i++ { + for i := range mapItemNumber { baseKeyName := makeKeyName(field.Type().Elem()) key := reflect.ValueOf(fmt.Sprintf("%s%d", baseKeyName, i)) // generate value - ptrType := reflect.PtrTo(field.Type().Elem()) + ptrType := reflect.PointerTo(field.Type().Elem()) ptrValue := reflect.New(ptrType) if err := fill(ptrValue); err != nil { return err @@ -125,7 +125,7 @@ func makeKeyName(typ reflect.Type) string { } func setStruct(field reflect.Value) error { - for i := 0; i < field.NumField(); i++ { + for i := range field.NumField() { fld := field.Field(i) stFld := field.Type().Field(i) @@ -142,7 +142,7 @@ func setStruct(field reflect.Value) error { func setSlice(field reflect.Value) error { field.Set(reflect.MakeSlice(field.Type(), sliceItemNumber, sliceItemNumber)) - for j := 0; j < field.Len(); j++ { + for j := range field.Len() { if err := fill(field.Index(j)); err != nil { return err } diff --git a/pkg/config/dynamic/config.go b/pkg/config/dynamic/config.go index 621c004e8..1e125ad78 100644 --- a/pkg/config/dynamic/config.go +++ b/pkg/config/dynamic/config.go @@ -24,7 +24,7 @@ type Configuration struct { HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"` TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty" export:"true"` UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty" export:"true"` - TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"-" export:"true"` + TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -32,6 +32,6 @@ type Configuration struct { // TLSConfiguration contains all the configuration parameters of a TLS connection. type TLSConfiguration struct { Certificates []*tls.CertAndStores `json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" label:"-" export:"true"` - Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"` + Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" label:"-" export:"true"` Stores map[string]tls.Store `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty" export:"true"` } diff --git a/pkg/config/kv/kv.go b/pkg/config/kv/kv.go index 8af4855e3..ad467b00d 100644 --- a/pkg/config/kv/kv.go +++ b/pkg/config/kv/kv.go @@ -55,7 +55,7 @@ func getFieldNames(rootName string, rootType reflect.Type) []string { return nil } - for i := 0; i < rootType.NumField(); i++ { + for i := range rootType.NumField() { field := rootType.Field(i) if !parser.IsExported(field) { diff --git a/pkg/config/label/label.go b/pkg/config/label/label.go index f6089a23f..ed1e71ddf 100644 --- a/pkg/config/label/label.go +++ b/pkg/config/label/label.go @@ -12,9 +12,11 @@ func DecodeConfiguration(labels map[string]string) (*dynamic.Configuration, erro HTTP: &dynamic.HTTPConfiguration{}, TCP: &dynamic.TCPConfiguration{}, UDP: &dynamic.UDPConfiguration{}, + TLS: &dynamic.TLSConfiguration{}, } - err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp", "traefik.udp") + // When decoding the TLS configuration we are making sure that only the default TLS store can be configured. + err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp", "traefik.udp", "traefik.tls.stores.default") if err != nil { return nil, err } diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index c6ffb1534..e1c168820 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/types" ) func Bool(v bool) *bool { return &v } @@ -217,6 +219,10 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.udp.routers.Router1.service": "foobar", "traefik.udp.services.Service0.loadbalancer.server.Port": "42", "traefik.udp.services.Service1.loadbalancer.server.Port": "42", + + "traefik.tls.stores.default.defaultgeneratedcert.resolver": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.main": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar", } configuration, err := DecodeConfiguration(labels) @@ -719,6 +725,19 @@ func TestDecodeConfiguration(t *testing.T) { }, }, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, } assert.Nil(t, configuration.HTTP.ServersTransports) @@ -1216,6 +1235,19 @@ func TestEncodeConfiguration(t *testing.T) { }, }, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, } labels, err := EncodeConfiguration(configuration) @@ -1415,6 +1447,10 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.TCP.Services.Service1.LoadBalancer.ServersTransport": "foo", "traefik.TCP.Services.Service1.LoadBalancer.TerminationDelay": "42", + "traefik.TLS.Stores.default.DefaultGeneratedCert.Resolver": "foobar", + "traefik.TLS.Stores.default.DefaultGeneratedCert.Domain.Main": "foobar", + "traefik.TLS.Stores.default.DefaultGeneratedCert.Domain.SANs": "foobar, fiibar", + "traefik.UDP.Routers.Router0.EntryPoints": "foobar, fiibar", "traefik.UDP.Routers.Router0.Service": "foobar", "traefik.UDP.Routers.Router1.EntryPoints": "foobar, fiibar", diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go index d7b155dbe..9b48ddce4 100644 --- a/pkg/config/static/entrypoints.go +++ b/pkg/config/static/entrypoints.go @@ -97,7 +97,7 @@ type RedirectEntryPoint struct { func (r *RedirectEntryPoint) SetDefaults() { r.Scheme = "https" r.Permanent = true - r.Priority = math.MaxInt32 - 1 + r.Priority = math.MaxInt - 1 } // TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point. diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 57af7a165..81d3401fd 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -193,10 +193,12 @@ 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"` - GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"` - SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"` - AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"` + ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` + GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"` + CapturedRequestHeaders []string `description:"Request headers to add as attributes for server and client spans." json:"capturedRequestHeaders,omitempty" toml:"capturedRequestHeaders,omitempty" yaml:"capturedRequestHeaders,omitempty" export:"true"` + CapturedResponseHeaders []string `description:"Response headers to add as attributes for server and client spans." json:"capturedResponseHeaders,omitempty" toml:"capturedResponseHeaders,omitempty" yaml:"capturedResponseHeaders,omitempty" export:"true"` + SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"` + AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"` OTLP *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } diff --git a/pkg/metrics/opentelemetry.go b/pkg/metrics/opentelemetry.go index cf9920a25..23cbf8eba 100644 --- a/pkg/metrics/opentelemetry.go +++ b/pkg/metrics/opentelemetry.go @@ -30,6 +30,74 @@ var ( openTelemetryGaugeCollector *gaugeCollector ) +// SetMeterProvider sets the meter provider for the tests. +func SetMeterProvider(meterProvider *sdkmetric.MeterProvider) { + openTelemetryMeterProvider = meterProvider + otel.SetMeterProvider(meterProvider) +} + +// SemConvMetricsRegistry holds stables semantic conventions metric instruments. +type SemConvMetricsRegistry struct { + // server metrics + httpServerRequestDuration metric.Float64Histogram + // client metrics + httpClientRequestDuration metric.Float64Histogram +} + +// NewSemConvMetricRegistry registers all stables semantic conventions metrics. +func NewSemConvMetricRegistry(ctx context.Context, config *types.OTLP) (*SemConvMetricsRegistry, error) { + 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, nil + } + } + + meter := otel.Meter("github.com/traefik/traefik", + metric.WithInstrumentationVersion(version.Version)) + + httpServerRequestDuration, err := meter.Float64Histogram("http.server.request.duration", + metric.WithDescription("Duration of HTTP server requests."), + metric.WithUnit("s"), + metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) + if err != nil { + return nil, fmt.Errorf("can't build httpServerRequestDuration histogram: %w", err) + } + + httpClientRequestDuration, err := meter.Float64Histogram("http.client.request.duration", + metric.WithDescription("Duration of HTTP client requests."), + metric.WithUnit("s"), + metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) + if err != nil { + return nil, fmt.Errorf("can't build httpClientRequestDuration histogram: %w", err) + } + + return &SemConvMetricsRegistry{ + httpServerRequestDuration: httpServerRequestDuration, + httpClientRequestDuration: httpClientRequestDuration, + }, nil +} + +// HTTPServerRequestDuration returns the HTTP server request duration histogram. +func (s *SemConvMetricsRegistry) HTTPServerRequestDuration() metric.Float64Histogram { + if s == nil { + return nil + } + + return s.httpServerRequestDuration +} + +// HTTPClientRequestDuration returns the HTTP client request duration histogram. +func (s *SemConvMetricsRegistry) HTTPClientRequestDuration() metric.Float64Histogram { + if s == nil { + return nil + } + + return s.httpClientRequestDuration +} + // RegisterOpenTelemetry registers all OpenTelemetry metrics. func RegisterOpenTelemetry(ctx context.Context, config *types.OTLP) Registry { if openTelemetryMeterProvider == nil { diff --git a/pkg/metrics/opentelemetry_test.go b/pkg/metrics/opentelemetry_test.go index 2e3ea328d..9d8da3cd6 100644 --- a/pkg/metrics/opentelemetry_test.go +++ b/pkg/metrics/opentelemetry_test.go @@ -361,7 +361,7 @@ func TestOpenTelemetry(t *testing.T) { expectedEntryPoints := []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_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","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`, `({"name":"traefik_entrypoint_requests_bytes_total","description":"The total size of requests in bytes handled by 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_responses_bytes_total","description":"The total size of responses in bytes handled by 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}})`, } @@ -377,7 +377,7 @@ func TestOpenTelemetry(t *testing.T) { expectedRouters := []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_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","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`, `({"name":"traefik_router_requests_bytes_total","description":"The total size of requests in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"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_responses_bytes_total","description":"The total size of responses in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"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}})`, } @@ -394,7 +394,7 @@ func TestOpenTelemetry(t *testing.T) { expectedServices := []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_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","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.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"}}\],"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`, `({"name":"traefik_service_requests_bytes_total","description":"The total size of requests in bytes received by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"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_responses_bytes_total","description":"The total size of responses in bytes returned by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`, @@ -426,7 +426,7 @@ func TestOpenTelemetry(t *testing.T) { // and as soon as the EntryPointReqDurationHistogram.Observe is called, // it adds a new dataPoint to the histogram. 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})`, + `({"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","0","0","0","2"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":20000})`, } registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(10000) diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index 4fce3deba..dddcb26a7 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -76,7 +76,7 @@ func TestLogRotation(t *testing.T) { halfDone := make(chan bool) writeDone := make(chan bool) go func() { - for i := 0; i < iterations; i++ { + for i := range iterations { handler.ServeHTTP(recorder, req) if i == iterations/2 { halfDone <- true diff --git a/pkg/middlewares/auth/basic_auth.go b/pkg/middlewares/auth/basic_auth.go index 5a3caa26d..0ec8f1e28 100644 --- a/pkg/middlewares/auth/basic_auth.go +++ b/pkg/middlewares/auth/basic_auth.go @@ -11,7 +11,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "go.opentelemetry.io/otel/trace" ) @@ -77,7 +77,7 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if !ok { logger.Debug().Msg("Authentication failed") - tracing.SetStatusErrorf(req.Context(), "Authentication failed") + observability.SetStatusErrorf(req.Context(), "Authentication failed") b.auth.RequireAuth(rw, req) return diff --git a/pkg/middlewares/auth/digest_auth.go b/pkg/middlewares/auth/digest_auth.go index e02bc5883..25c22865f 100644 --- a/pkg/middlewares/auth/digest_auth.go +++ b/pkg/middlewares/auth/digest_auth.go @@ -11,7 +11,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "go.opentelemetry.io/otel/trace" ) @@ -78,13 +78,13 @@ func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if authinfo != nil && *authinfo == "stale" { logger.Debug().Msg("Digest authentication failed, possibly because out of order requests") - tracing.SetStatusErrorf(req.Context(), "Digest authentication failed, possibly because out of order requests") + observability.SetStatusErrorf(req.Context(), "Digest authentication failed, possibly because out of order requests") d.auth.RequireAuthStale(rw, req) return } logger.Debug().Msg("Digest authentication failed") - tracing.SetStatusErrorf(req.Context(), "Digest authentication failed") + observability.SetStatusErrorf(req.Context(), "Digest authentication failed") d.auth.RequireAuth(rw, req) return } diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index daa2dbd27..26053a50e 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -14,6 +14,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/connectionheader" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/types" "github.com/vulcand/oxy/v2/forward" @@ -126,13 +127,16 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if err != nil { logMessage := fmt.Sprintf("Error calling %s. Cause %s", fa.address, err) logger.Debug().Msg(logMessage) - tracing.SetStatusErrorf(req.Context(), logMessage) + observability.SetStatusErrorf(req.Context(), logMessage) rw.WriteHeader(http.StatusInternalServerError) return } + writeHeader(req, forwardReq, fa.trustForwardHeader, fa.authRequestHeaders) + var forwardSpan trace.Span - if tracer := tracing.TracerFromContext(req.Context()); tracer != nil { + var tracer *tracing.Tracer + if tracer = tracing.TracerFromContext(req.Context()); tracer != nil { var tracingCtx context.Context tracingCtx, forwardSpan = tracer.Start(req.Context(), "AuthRequest", trace.WithSpanKind(trace.SpanKindClient)) defer forwardSpan.End() @@ -140,16 +144,14 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { forwardReq = forwardReq.WithContext(tracingCtx) tracing.InjectContextIntoCarrier(forwardReq) - tracing.LogClientRequest(forwardSpan, forwardReq) + tracer.CaptureClientRequest(forwardSpan, forwardReq) } - writeHeader(req, forwardReq, fa.trustForwardHeader, fa.authRequestHeaders) - forwardResponse, forwardErr := fa.client.Do(forwardReq) if forwardErr != nil { logMessage := fmt.Sprintf("Error calling %s. Cause: %s", fa.address, forwardErr) logger.Debug().Msg(logMessage) - tracing.SetStatusErrorf(forwardReq.Context(), logMessage) + observability.SetStatusErrorf(forwardReq.Context(), logMessage) rw.WriteHeader(http.StatusInternalServerError) return @@ -160,7 +162,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if readError != nil { logMessage := fmt.Sprintf("Error reading body %s. Cause: %s", fa.address, readError) logger.Debug().Msg(logMessage) - tracing.SetStatusErrorf(forwardReq.Context(), logMessage) + observability.SetStatusErrorf(forwardReq.Context(), logMessage) rw.WriteHeader(http.StatusInternalServerError) return @@ -187,7 +189,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if !errors.Is(err, http.ErrNoLocation) { logMessage := fmt.Sprintf("Error reading response location header %s. Cause: %s", fa.address, err) logger.Debug().Msg(logMessage) - tracing.SetStatusErrorf(forwardReq.Context(), logMessage) + observability.SetStatusErrorf(forwardReq.Context(), logMessage) rw.WriteHeader(http.StatusInternalServerError) return @@ -197,7 +199,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Location", redirectURL.String()) } - tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient) + tracer.CaptureResponse(forwardSpan, forwardResponse.Header, forwardResponse.StatusCode, trace.SpanKindClient) rw.WriteHeader(forwardResponse.StatusCode) if _, err = rw.Write(body); err != nil { @@ -228,7 +230,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } - tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient) + tracer.CaptureResponse(forwardSpan, forwardResponse.Header, forwardResponse.StatusCode, trace.SpanKindClient) req.RequestURI = req.URL.RequestURI() diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go index 57e07314f..ed3e2bb2b 100644 --- a/pkg/middlewares/auth/forward_test.go +++ b/pkg/middlewares/auth/forward_test.go @@ -4,27 +4,25 @@ import ( "context" "fmt" "io" + "net" "net/http" "net/http/httptest" + "net/url" + "strconv" "testing" - "github.com/containous/alice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/config/static" - tracingMiddleware "github.com/traefik/traefik/v3/pkg/middlewares/tracing" "github.com/traefik/traefik/v3/pkg/testhelpers" "github.com/traefik/traefik/v3/pkg/tracing" - "github.com/traefik/traefik/v3/pkg/tracing/opentelemetry" - "github.com/traefik/traefik/v3/pkg/types" - "github.com/traefik/traefik/v3/pkg/version" "github.com/vulcand/oxy/v2/forward" + "go.opentelemetry.io/contrib/propagators/autoprop" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/embedded" ) func TestForwardAuthFail(t *testing.T) { @@ -466,64 +464,146 @@ func Test_writeHeader(t *testing.T) { } } -func TestForwardAuthUsesTracing(t *testing.T) { +func TestForwardAuthTracing(t *testing.T) { + type expected struct { + name string + attributes []attribute.KeyValue + } + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Traceparent") == "" { t.Errorf("expected Traceparent header to be present in request") } + + w.Header().Set("X-Bar", "foo") + w.Header().Add("X-Bar", "bar") + w.WriteHeader(http.StatusNotFound) })) t.Cleanup(server.Close) - next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - - auth := dynamic.ForwardAuth{ - Address: server.URL, - } - - exporter := tracetest.NewInMemoryExporter() - - tres, err := resource.New(context.Background(), - resource.WithAttributes(semconv.ServiceNameKey.String("traefik")), - resource.WithAttributes(semconv.ServiceVersionKey.String(version.Version)), - resource.WithFromEnv(), - resource.WithTelemetrySDK(), - ) + parse, err := url.Parse(server.URL) require.NoError(t, err) - tracerProvider := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithResource(tres), - sdktrace.WithBatcher(exporter), - ) - otel.SetTracerProvider(tracerProvider) + _, serverPort, err := net.SplitHostPort(parse.Host) + require.NoError(t, err) - config := &static.Tracing{ - ServiceName: "testApp", - SampleRate: 1, - OTLP: &opentelemetry.Config{ - HTTP: &types.OtelHTTP{ - Endpoint: "http://127.0.0.1:8080", + serverPortInt, err := strconv.Atoi(serverPort) + require.NoError(t, err) + + testCases := []struct { + desc string + expected []expected + }{ + { + desc: "basic test", + expected: []expected{ + { + name: "initial", + attributes: []attribute.KeyValue{ + attribute.String("span.kind", "unspecified"), + }, + }, + { + name: "AuthRequest", + attributes: []attribute.KeyValue{ + attribute.String("span.kind", "client"), + attribute.String("http.request.method", "GET"), + attribute.String("network.protocol.version", "1.1"), + attribute.String("url.full", server.URL), + attribute.String("url.scheme", "http"), + attribute.String("user_agent.original", ""), + attribute.String("network.peer.address", "127.0.0.1"), + attribute.String("network.peer.port", serverPort), + attribute.String("server.address", "127.0.0.1"), + attribute.Int64("server.port", int64(serverPortInt)), + attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}), + attribute.Int64("http.response.status_code", int64(404)), + attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}), + }, + }, }, }, } - tr, closer, err := tracing.NewTracing(config) - require.NoError(t, err) - t.Cleanup(func() { - _ = closer.Close() - }) - next, err = NewForward(context.Background(), next, auth, "authTest") - require.NoError(t, err) + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - chain := alice.New(tracingMiddleware.WrapEntryPointHandler(context.Background(), tr, "tracingTest")) - next, err = chain.Then(next) - require.NoError(t, err) + auth := dynamic.ForwardAuth{ + Address: server.URL, + AuthRequestHeaders: []string{"X-Foo"}, + } + next, err := NewForward(context.Background(), next, auth, "authTest") + require.NoError(t, err) - ts := httptest.NewServer(next) - t.Cleanup(ts.Close) + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) + req.RemoteAddr = "10.0.0.1:1234" + req.Header.Set("User-Agent", "forward-test") + req.Header.Set("X-Forwarded-Proto", "http") + req.Header.Set("X-Foo", "foo") + req.Header.Add("X-Foo", "bar") - req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) - res, err := http.DefaultClient.Do(req) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) + otel.SetTextMapPropagator(autoprop.NewTextMapPropagator()) + + mockTracer := &mockTracer{} + tracer := tracing.NewTracer(mockTracer, []string{"X-Foo"}, []string{"X-Bar"}) + initialCtx, initialSpan := tracer.Start(req.Context(), "initial") + defer initialSpan.End() + req = req.WithContext(initialCtx) + + recorder := httptest.NewRecorder() + next.ServeHTTP(recorder, req) + + for i, span := range mockTracer.spans { + assert.Equal(t, test.expected[i].name, span.name) + assert.Equal(t, test.expected[i].attributes, span.attributes) + } + }) + } +} + +type mockTracer struct { + embedded.Tracer + + spans []*mockSpan +} + +var _ trace.Tracer = &mockTracer{} + +func (t *mockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + config := trace.NewSpanStartConfig(opts...) + span := &mockSpan{} + span.SetName(name) + span.SetAttributes(attribute.String("span.kind", config.SpanKind().String())) + span.SetAttributes(config.Attributes()...) + t.spans = append(t.spans, span) + return trace.ContextWithSpan(ctx, span), span +} + +// mockSpan is an implementation of Span that preforms no operations. +type mockSpan struct { + embedded.Span + + name string + attributes []attribute.KeyValue +} + +var _ trace.Span = &mockSpan{} + +func (*mockSpan) SpanContext() trace.SpanContext { + return trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID{1}, SpanID: trace.SpanID{1}}) +} +func (*mockSpan) IsRecording() bool { return false } +func (s *mockSpan) SetStatus(_ codes.Code, _ string) {} +func (s *mockSpan) SetAttributes(kv ...attribute.KeyValue) { + s.attributes = append(s.attributes, kv...) +} +func (s *mockSpan) End(...trace.SpanEndOption) {} +func (s *mockSpan) RecordError(_ error, _ ...trace.EventOption) {} +func (s *mockSpan) AddEvent(_ string, _ ...trace.EventOption) {} + +func (s *mockSpan) SetName(name string) { s.name = name } + +func (s *mockSpan) TracerProvider() trace.TracerProvider { + return nil } diff --git a/pkg/middlewares/capture/capture_test.go b/pkg/middlewares/capture/capture_test.go index 5c4a437cb..a22c1464d 100644 --- a/pkg/middlewares/capture/capture_test.go +++ b/pkg/middlewares/capture/capture_test.go @@ -149,7 +149,7 @@ func BenchmarkCapture(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(test.size)) b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { runBenchmark(b, test.size, req, handlers) } }) @@ -170,7 +170,7 @@ func runBenchmark(b *testing.B, size int, req *http.Request, handler http.Handle func generateBytes(length int) []byte { var value []byte - for i := 0; i < length; i++ { + for i := range length { value = append(value, 0x61+byte(i%26)) } return value diff --git a/pkg/middlewares/circuitbreaker/circuit_breaker.go b/pkg/middlewares/circuitbreaker/circuit_breaker.go index ba377ea3d..ceaa93de4 100644 --- a/pkg/middlewares/circuitbreaker/circuit_breaker.go +++ b/pkg/middlewares/circuitbreaker/circuit_breaker.go @@ -10,7 +10,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/vulcand/oxy/v2/cbreaker" "go.opentelemetry.io/otel/trace" ) @@ -34,7 +34,7 @@ func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.Circ cbOpts := []cbreaker.Option{ cbreaker.Fallback(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - tracing.SetStatusErrorf(req.Context(), "blocked by circuit-breaker (%q)", expression) + observability.SetStatusErrorf(req.Context(), "blocked by circuit-breaker (%q)", expression) rw.WriteHeader(responseCode) if _, err := rw.Write([]byte(http.StatusText(responseCode))); err != nil { diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index 722001da3..34e1ea7fc 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -720,7 +720,7 @@ func BenchmarkCompress(b *testing.B) { } b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { runBenchmark(b, req, handler) } }) @@ -741,7 +741,7 @@ func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) { func generateBytes(length int) []byte { var value []byte - for i := 0; i < length; i++ { + for i := range length { value = append(value, 0x61+byte(i)) } return value diff --git a/pkg/middlewares/customerrors/custom_errors.go b/pkg/middlewares/customerrors/custom_errors.go index dd1f77e33..dbd66718a 100644 --- a/pkg/middlewares/customerrors/custom_errors.go +++ b/pkg/middlewares/customerrors/custom_errors.go @@ -12,7 +12,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/types" "github.com/vulcand/oxy/v2/utils" "go.opentelemetry.io/otel/trace" @@ -71,7 +71,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if c.backendHandler == nil { logger.Error().Msg("Error pages: no backend handler.") - tracing.SetStatusErrorf(req.Context(), "Error pages: no backend handler.") + observability.SetStatusErrorf(req.Context(), "Error pages: no backend handler.") c.next.ServeHTTP(rw, req) return } @@ -96,7 +96,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { pageReq, err := newRequest("http://" + req.Host + query) if err != nil { logger.Error().Err(err).Send() - tracing.SetStatusErrorf(req.Context(), err.Error()) + observability.SetStatusErrorf(req.Context(), err.Error()) http.Error(rw, http.StatusText(code), code) return } diff --git a/pkg/middlewares/ipallowlist/ip_allowlist.go b/pkg/middlewares/ipallowlist/ip_allowlist.go index 3841f3f23..d7cee8443 100644 --- a/pkg/middlewares/ipallowlist/ip_allowlist.go +++ b/pkg/middlewares/ipallowlist/ip_allowlist.go @@ -10,7 +10,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/ip" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "go.opentelemetry.io/otel/trace" ) @@ -78,7 +78,7 @@ func (al *ipAllowLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if err != nil { msg := fmt.Sprintf("Rejecting IP %s: %v", clientIP, err) logger.Debug().Msg(msg) - tracing.SetStatusErrorf(req.Context(), msg) + observability.SetStatusErrorf(req.Context(), msg) reject(ctx, al.rejectStatusCode, rw) return } diff --git a/pkg/middlewares/ipwhitelist/ip_whitelist.go b/pkg/middlewares/ipwhitelist/ip_whitelist.go index 937bde420..6a74aebfb 100644 --- a/pkg/middlewares/ipwhitelist/ip_whitelist.go +++ b/pkg/middlewares/ipwhitelist/ip_whitelist.go @@ -10,7 +10,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/ip" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "go.opentelemetry.io/otel/trace" ) @@ -68,7 +68,7 @@ func (wl *ipWhiteLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if err != nil { msg := fmt.Sprintf("Rejecting IP %s: %v", clientIP, err) logger.Debug().Msg(msg) - tracing.SetStatusErrorf(req.Context(), msg) + observability.SetStatusErrorf(req.Context(), msg) reject(ctx, rw) return } diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go index a61da207e..1bbe4798d 100644 --- a/pkg/middlewares/metrics/metrics.go +++ b/pkg/middlewares/metrics/metrics.go @@ -3,6 +3,7 @@ package metrics import ( "context" "net/http" + "slices" "strconv" "strings" "time" @@ -14,9 +15,9 @@ import ( "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/capture" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/retry" traefiktls "github.com/traefik/traefik/v3/pkg/tls" - "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/codes" ) @@ -144,7 +145,7 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) } logger := with.Logger() logger.Error().Err(err).Msg("Could not get Capture") - tracing.SetStatusErrorf(req.Context(), "Could not get Capture") + observability.SetStatusErrorf(req.Context(), "Could not get Capture") return } @@ -216,12 +217,10 @@ func grpcStatusCode(rw http.ResponseWriter) int { func containsHeader(req *http.Request, name, value string) bool { items := strings.Split(req.Header.Get(name), ",") - for _, item := range items { - if value == strings.ToLower(strings.TrimSpace(item)) { - return true - } - } - return false + + return slices.ContainsFunc(items, func(item string) bool { + return value == strings.ToLower(strings.TrimSpace(item)) + }) } // getMethod returns the request's method. diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go new file mode 100644 index 000000000..5de208a25 --- /dev/null +++ b/pkg/middlewares/observability/entrypoint.go @@ -0,0 +1,98 @@ +package observability + +import ( + "context" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/containous/alice" + "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/middlewares" + "github.com/traefik/traefik/v3/pkg/tracing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" +) + +const ( + entryPointTypeName = "TracingEntryPoint" +) + +type entryPointTracing struct { + tracer *tracing.Tracer + + entryPoint string + next http.Handler + semConvMetricRegistry *metrics.SemConvMetricsRegistry +} + +// WrapEntryPointHandler Wraps tracing to alice.Constructor. +func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string) alice.Constructor { + return func(next http.Handler) (http.Handler, error) { + if tracer == nil { + tracer = tracing.NewTracer(noop.Tracer{}, nil, nil) + } + + return newEntryPoint(ctx, tracer, semConvMetricRegistry, entryPointName, next), nil + } +} + +// newEntryPoint creates a new tracing middleware for incoming requests. +func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string, next http.Handler) http.Handler { + middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware") + + if tracer == nil { + tracer = tracing.NewTracer(noop.Tracer{}, nil, nil) + } + + return &entryPointTracing{ + entryPoint: entryPointName, + tracer: tracer, + semConvMetricRegistry: semConvMetricRegistry, + next: next, + } +} + +func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + tracingCtx := tracing.ExtractCarrierIntoContext(req.Context(), req.Header) + start := time.Now() + tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start)) + + req = req.WithContext(tracingCtx) + + span.SetAttributes(attribute.String("entry_point", e.entryPoint)) + + e.tracer.CaptureServerRequest(span, req) + + recorder := newStatusCodeRecorder(rw, http.StatusOK) + e.next.ServeHTTP(recorder, req) + + e.tracer.CaptureResponse(span, recorder.Header(), recorder.Status(), trace.SpanKindServer) + + end := time.Now() + span.End(trace.WithTimestamp(end)) + + if e.semConvMetricRegistry != nil && e.semConvMetricRegistry.HTTPServerRequestDuration() != nil { + var attrs []attribute.KeyValue + + if recorder.Status() < 100 || recorder.Status() >= 600 { + attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code ; %d", recorder.Status()))) + } else if recorder.Status() >= 400 { + attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(recorder.Status()))) + } + + attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method)) + attrs = append(attrs, semconv.HTTPResponseStatusCode(recorder.Status())) + attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto))) + attrs = append(attrs, semconv.NetworkProtocolVersion(Proto(req.Proto))) + attrs = append(attrs, semconv.ServerAddress(req.Host)) + attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto"))) + + e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...)) + } +} diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go new file mode 100644 index 000000000..5fc9dde08 --- /dev/null +++ b/pkg/middlewares/observability/entrypoint_test.go @@ -0,0 +1,185 @@ +package observability + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/types" + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" +) + +func TestEntryPointMiddleware_tracing(t *testing.T) { + type expected struct { + name string + attributes []attribute.KeyValue + } + + testCases := []struct { + desc string + entryPoint string + expected expected + }{ + { + desc: "basic test", + entryPoint: "test", + expected: expected{ + name: "EntryPoint", + attributes: []attribute.KeyValue{ + attribute.String("span.kind", "server"), + attribute.String("entry_point", "test"), + attribute.String("http.request.method", "GET"), + attribute.String("network.protocol.version", "1.1"), + attribute.Int64("http.request.body.size", int64(0)), + attribute.String("url.path", "/search"), + attribute.String("url.query", "q=Opentelemetry"), + attribute.String("url.scheme", "http"), + attribute.String("user_agent.original", "entrypoint-test"), + attribute.String("server.address", "www.test.com"), + attribute.String("network.peer.address", "10.0.0.1"), + attribute.String("network.peer.port", "1234"), + attribute.String("client.address", "10.0.0.1"), + attribute.Int64("client.port", int64(1234)), + attribute.String("client.socket.address", ""), + attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}), + attribute.Int64("http.response.status_code", int64(404)), + attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}), + }, + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) + rw := httptest.NewRecorder() + req.RemoteAddr = "10.0.0.1:1234" + req.Header.Set("User-Agent", "entrypoint-test") + req.Header.Set("X-Forwarded-Proto", "http") + req.Header.Set("X-Foo", "foo") + req.Header.Add("X-Foo", "bar") + + next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.Header().Set("X-Bar", "foo") + rw.Header().Add("X-Bar", "bar") + rw.WriteHeader(http.StatusNotFound) + }) + + tracer := &mockTracer{} + + handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}), nil, test.entryPoint, next) + handler.ServeHTTP(rw, req) + + for _, span := range tracer.spans { + assert.Equal(t, test.expected.name, span.name) + assert.Equal(t, test.expected.attributes, span.attributes) + } + }) + } +} + +func TestEntryPointMiddleware_metrics(t *testing.T) { + tests := []struct { + desc string + statusCode int + wantAttributes attribute.Set + }{ + { + desc: "not found status", + statusCode: http.StatusNotFound, + wantAttributes: attribute.NewSet( + attribute.Key("error.type").String("404"), + attribute.Key("http.request.method").String("GET"), + attribute.Key("http.response.status_code").Int(404), + attribute.Key("network.protocol.name").String("http/1.1"), + attribute.Key("network.protocol.version").String("1.1"), + attribute.Key("server.address").String("www.test.com"), + attribute.Key("url.scheme").String("http"), + ), + }, + { + desc: "created status", + statusCode: http.StatusCreated, + wantAttributes: attribute.NewSet( + attribute.Key("http.request.method").String("GET"), + attribute.Key("http.response.status_code").Int(201), + attribute.Key("network.protocol.name").String("http/1.1"), + attribute.Key("network.protocol.version").String("1.1"), + attribute.Key("server.address").String("www.test.com"), + attribute.Key("url.scheme").String("http"), + ), + }, + } + + for _, test := range tests { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var cfg types.OTLP + (&cfg).SetDefaults() + cfg.AddRoutersLabels = true + cfg.PushInterval = ptypes.Duration(10 * time.Millisecond) + rdr := sdkmetric.NewManualReader() + + meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(rdr)) + // force the meter provider with manual reader to collect metrics for the test. + metrics.SetMeterProvider(meterProvider) + + semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg) + require.NoError(t, err) + require.NotNil(t, semConvMetricRegistry) + + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) + rw := httptest.NewRecorder() + req.RemoteAddr = "10.0.0.1:1234" + req.Header.Set("User-Agent", "entrypoint-test") + req.Header.Set("X-Forwarded-Proto", "http") + + next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(test.statusCode) + }) + + handler := newEntryPoint(context.Background(), nil, semConvMetricRegistry, "test", next) + handler.ServeHTTP(rw, req) + + got := metricdata.ResourceMetrics{} + err = rdr.Collect(context.Background(), &got) + require.NoError(t, err) + + require.Len(t, got.ScopeMetrics, 1) + + expected := metricdata.Metrics{ + Name: "http.server.request.duration", + Description: "Duration of HTTP server requests.", + Unit: "s", + Data: metricdata.Histogram[float64]{ + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + Attributes: test.wantAttributes, + Count: 1, + Bounds: []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10}, + BucketCounts: []uint64{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + Min: metricdata.NewExtrema[float64](1), + Max: metricdata.NewExtrema[float64](1), + Sum: 1, + }, + }, + Temporality: metricdata.CumulativeTemporality, + }, + } + + metricdatatest.AssertEqual[metricdata.Metrics](t, expected, got.ScopeMetrics[0].Metrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue()) + }) + } +} diff --git a/pkg/middlewares/tracing/middleware.go b/pkg/middlewares/observability/middleware.go similarity index 98% rename from pkg/middlewares/tracing/middleware.go rename to pkg/middlewares/observability/middleware.go index 47a6af502..51e4b6d50 100644 --- a/pkg/middlewares/tracing/middleware.go +++ b/pkg/middlewares/observability/middleware.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" diff --git a/pkg/middlewares/tracing/mock_tracing_test.go b/pkg/middlewares/observability/mock_tracing_test.go similarity index 86% rename from pkg/middlewares/tracing/mock_tracing_test.go rename to pkg/middlewares/observability/mock_tracing_test.go index a44345f6a..31ddbc642 100644 --- a/pkg/middlewares/tracing/mock_tracing_test.go +++ b/pkg/middlewares/observability/mock_tracing_test.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" @@ -47,7 +47,9 @@ type mockSpan struct { var _ trace.Span = &mockSpan{} -func (*mockSpan) SpanContext() trace.SpanContext { return trace.SpanContext{} } +func (*mockSpan) SpanContext() trace.SpanContext { + return trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID{1}, SpanID: trace.SpanID{1}}) +} func (*mockSpan) IsRecording() bool { return false } func (s *mockSpan) SetStatus(_ codes.Code, _ string) {} func (s *mockSpan) SetAttributes(kv ...attribute.KeyValue) { @@ -59,4 +61,6 @@ func (s *mockSpan) AddEvent(_ string, _ ...trace.EventOption) {} func (s *mockSpan) SetName(name string) { s.name = name } -func (*mockSpan) TracerProvider() trace.TracerProvider { return mockTracerProvider{} } +func (s *mockSpan) TracerProvider() trace.TracerProvider { + return nil +} diff --git a/pkg/middlewares/observability/observability.go b/pkg/middlewares/observability/observability.go new file mode 100644 index 000000000..54f243186 --- /dev/null +++ b/pkg/middlewares/observability/observability.go @@ -0,0 +1,31 @@ +package observability + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +// SetStatusErrorf flags the span as in error and log an event. +func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) { + if span := trace.SpanFromContext(ctx); span != nil { + span.SetStatus(codes.Error, fmt.Sprintf(format, args...)) + } +} + +func Proto(proto string) string { + switch proto { + case "HTTP/1.0": + return "1.0" + case "HTTP/1.1": + return "1.1" + case "HTTP/2": + return "2" + case "HTTP/3": + return "3" + default: + return proto + } +} diff --git a/pkg/middlewares/tracing/router.go b/pkg/middlewares/observability/router.go similarity index 98% rename from pkg/middlewares/tracing/router.go rename to pkg/middlewares/observability/router.go index e964be12c..b9e69d78e 100644 --- a/pkg/middlewares/tracing/router.go +++ b/pkg/middlewares/observability/router.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" diff --git a/pkg/middlewares/tracing/router_test.go b/pkg/middlewares/observability/router_test.go similarity index 99% rename from pkg/middlewares/tracing/router_test.go rename to pkg/middlewares/observability/router_test.go index b220cdabe..23bb46ab9 100644 --- a/pkg/middlewares/tracing/router_test.go +++ b/pkg/middlewares/observability/router_test.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" diff --git a/pkg/middlewares/tracing/service.go b/pkg/middlewares/observability/service.go similarity index 98% rename from pkg/middlewares/tracing/service.go rename to pkg/middlewares/observability/service.go index 9cc5bd1ca..cacd3ef1b 100644 --- a/pkg/middlewares/tracing/service.go +++ b/pkg/middlewares/observability/service.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" diff --git a/pkg/middlewares/tracing/service_test.go b/pkg/middlewares/observability/service_test.go similarity index 98% rename from pkg/middlewares/tracing/service_test.go rename to pkg/middlewares/observability/service_test.go index c78510faf..db411e718 100644 --- a/pkg/middlewares/tracing/service_test.go +++ b/pkg/middlewares/observability/service_test.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "context" diff --git a/pkg/middlewares/tracing/status_code.go b/pkg/middlewares/observability/status_code.go similarity index 97% rename from pkg/middlewares/tracing/status_code.go rename to pkg/middlewares/observability/status_code.go index b30e6d034..ebada5579 100644 --- a/pkg/middlewares/tracing/status_code.go +++ b/pkg/middlewares/observability/status_code.go @@ -1,4 +1,4 @@ -package tracing +package observability import ( "bufio" diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go index fcede7dce..0b1a6aaa1 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter.go +++ b/pkg/middlewares/ratelimiter/rate_limiter.go @@ -12,7 +12,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/vulcand/oxy/v2/utils" "go.opentelemetry.io/otel/trace" "golang.org/x/time/rate" @@ -153,14 +153,14 @@ func (rl *rateLimiter) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // as the expiryTime is supposed to reflect the activity (or lack thereof) on that source. if err := rl.buckets.Set(source, bucket, rl.ttl); err != nil { logger.Error().Err(err).Msg("Could not insert/update bucket") - tracing.SetStatusErrorf(req.Context(), "Could not insert/update bucket") + observability.SetStatusErrorf(req.Context(), "Could not insert/update bucket") http.Error(rw, "could not insert/update bucket", http.StatusInternalServerError) return } res := bucket.Reserve() if !res.OK() { - tracing.SetStatusErrorf(req.Context(), "No bursty traffic allowed") + observability.SetStatusErrorf(req.Context(), "No bursty traffic allowed") http.Error(rw, "No bursty traffic allowed", http.StatusTooManyRequests) return } diff --git a/pkg/middlewares/replacepath/replace_path.go b/pkg/middlewares/replacepath/replace_path.go index 3419d7b83..662c5f5d3 100644 --- a/pkg/middlewares/replacepath/replace_path.go +++ b/pkg/middlewares/replacepath/replace_path.go @@ -7,7 +7,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "go.opentelemetry.io/otel/trace" ) @@ -52,7 +52,7 @@ func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) { req.URL.Path, err = url.PathUnescape(req.URL.RawPath) if err != nil { middlewares.GetLogger(context.Background(), r.name, typeName).Error().Err(err).Send() - tracing.SetStatusErrorf(req.Context(), err.Error()) + observability.SetStatusErrorf(req.Context(), err.Error()) http.Error(rw, err.Error(), http.StatusInternalServerError) return } diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go index ff800d354..16c131f8c 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex.go @@ -10,8 +10,8 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/replacepath" - "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/trace" ) @@ -63,7 +63,7 @@ func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) req.URL.Path, err = url.PathUnescape(req.URL.RawPath) if err != nil { middlewares.GetLogger(context.Background(), rp.name, typeName).Error().Err(err).Send() - tracing.SetStatusErrorf(req.Context(), err.Error()) + observability.SetStatusErrorf(req.Context(), err.Error()) http.Error(rw, err.Error(), http.StatusInternalServerError) return } diff --git a/pkg/middlewares/requestdecorator/hostresolver.go b/pkg/middlewares/requestdecorator/hostresolver.go index 028231f1f..4fd5f5f3c 100644 --- a/pkg/middlewares/requestdecorator/hostresolver.go +++ b/pkg/middlewares/requestdecorator/hostresolver.go @@ -49,7 +49,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string { logger := log.Ctx(ctx) cacheDuration := 0 * time.Second - for depth := 0; depth < hr.ResolvDepth; depth++ { + for depth := range hr.ResolvDepth { resolv, err := cnameResolve(ctx, request, hr.ResolvConfig) if err != nil { logger.Error().Err(err).Send() diff --git a/pkg/middlewares/retry/retry_test.go b/pkg/middlewares/retry/retry_test.go index eaeeea1ed..010376b86 100644 --- a/pkg/middlewares/retry/retry_test.go +++ b/pkg/middlewares/retry/retry_test.go @@ -203,7 +203,7 @@ func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) { } // Validate that we don't have headers from previous attempts - for i := 0; i < attempt; i++ { + for i := range attempt { headerName := fmt.Sprintf("X-Foo-Test-%d", i) headerValue = responseRecorder.Header().Get("headerName") if headerValue != "" { diff --git a/pkg/middlewares/tracing/entrypoint.go b/pkg/middlewares/tracing/entrypoint.go deleted file mode 100644 index 5ea32ee8f..000000000 --- a/pkg/middlewares/tracing/entrypoint.go +++ /dev/null @@ -1,57 +0,0 @@ -package tracing - -import ( - "context" - "net/http" - - "github.com/containous/alice" - "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -const ( - entryPointTypeName = "TracingEntryPoint" -) - -type entryPointTracing struct { - tracer trace.Tracer - entryPoint string - next http.Handler -} - -// WrapEntryPointHandler Wraps tracing to alice.Constructor. -func WrapEntryPointHandler(ctx context.Context, tracer trace.Tracer, entryPointName string) alice.Constructor { - return func(next http.Handler) (http.Handler, error) { - return newEntryPoint(ctx, tracer, entryPointName, next), nil - } -} - -// newEntryPoint creates a new tracing middleware for incoming requests. -func newEntryPoint(ctx context.Context, tracer trace.Tracer, entryPointName string, next http.Handler) http.Handler { - middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware") - - return &entryPointTracing{ - entryPoint: entryPointName, - tracer: tracer, - next: next, - } -} - -func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - tracingCtx := tracing.ExtractCarrierIntoContext(req.Context(), req.Header) - tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer)) - defer span.End() - - req = req.WithContext(tracingCtx) - - span.SetAttributes(attribute.String("entry_point", e.entryPoint)) - - tracing.LogServerRequest(span, req) - - recorder := newStatusCodeRecorder(rw, http.StatusOK) - e.next.ServeHTTP(recorder, req) - - tracing.LogResponseCode(span, recorder.Status(), trace.SpanKindServer) -} diff --git a/pkg/middlewares/tracing/entrypoint_test.go b/pkg/middlewares/tracing/entrypoint_test.go deleted file mode 100644 index b3cb60d67..000000000 --- a/pkg/middlewares/tracing/entrypoint_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package tracing - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/otel/attribute" -) - -func TestEntryPointMiddleware(t *testing.T) { - type expected struct { - name string - attributes []attribute.KeyValue - } - - testCases := []struct { - desc string - entryPoint string - expected expected - }{ - { - desc: "basic test", - entryPoint: "test", - expected: expected{ - name: "EntryPoint", - attributes: []attribute.KeyValue{ - attribute.String("span.kind", "server"), - attribute.String("entry_point", "test"), - attribute.String("http.request.method", "GET"), - attribute.String("network.protocol.version", "1.1"), - attribute.Int64("http.request.body.size", int64(0)), - attribute.String("url.path", "/search"), - attribute.String("url.query", "q=Opentelemetry"), - attribute.String("url.scheme", "http"), - attribute.String("user_agent.original", "entrypoint-test"), - attribute.String("server.address", "www.test.com"), - attribute.String("network.peer.address", "10.0.0.1"), - attribute.String("network.peer.port", "1234"), - attribute.String("client.address", "10.0.0.1"), - attribute.Int64("client.port", int64(1234)), - attribute.String("client.socket.address", ""), - attribute.Int64("http.response.status_code", int64(404)), - }, - }, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) - rw := httptest.NewRecorder() - req.RemoteAddr = "10.0.0.1:1234" - req.Header.Set("User-Agent", "entrypoint-test") - req.Header.Set("X-Forwarded-Proto", "http") - - next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { - rw.WriteHeader(http.StatusNotFound) - }) - - tracer := &mockTracer{} - - handler := newEntryPoint(context.Background(), tracer, test.entryPoint, next) - handler.ServeHTTP(rw, req) - - for _, span := range tracer.spans { - assert.Equal(t, test.expected.name, span.name) - assert.Equal(t, test.expected.attributes, span.attributes) - } - }) - } -} diff --git a/pkg/provider/acme/challenge_tls.go b/pkg/provider/acme/challenge_tls.go index f196e8952..cb15c3d3f 100644 --- a/pkg/provider/acme/challenge_tls.go +++ b/pkg/provider/acme/challenge_tls.go @@ -2,6 +2,7 @@ package acme import ( "fmt" + "slices" "sync" "time" @@ -119,7 +120,7 @@ func (c *ChallengeTLSALPN) ListenConfiguration(conf dynamic.Configuration) { c.muChans.Lock() for _, certificate := range conf.TLS.Certificates { - if !containsACMETLS1(certificate.Stores) { + if !slices.Contains(certificate.Stores, tlsalpn01.ACMETLS1Protocol) { continue } @@ -165,13 +166,3 @@ func createMessage(certs map[string]*Certificate) dynamic.Message { return conf } - -func containsACMETLS1(stores []string) bool { - for _, store := range stores { - if store == tlsalpn01.ACMETLS1Protocol { - return true - } - } - - return false -} diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index 12d5a0b6d..34f5e12d7 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -33,9 +33,6 @@ import ( const resolverSuffix = ".acme" -// ocspMustStaple enables OCSP stapling as from https://github.com/go-acme/lego/issues/270. -var ocspMustStaple = false - // Configuration holds ACME configuration provided by users. type Configuration struct { Email string `description:"Email address used for registration." json:"email,omitempty" toml:"email,omitempty" yaml:"email,omitempty"` @@ -427,7 +424,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) { if len(route.TLS.Domains) > 0 { domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains) - for i := 0; i < len(domains); i++ { + for i := range len(domains) { domain := domains[i] safe.Go(func() { dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName) @@ -464,7 +461,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) { if len(route.TLS.Domains) > 0 { domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains) - for i := 0; i < len(domains); i++ { + for i := range len(domains) { domain := domains[i] safe.Go(func() { dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName) @@ -585,7 +582,6 @@ func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []stri request := certificate.ObtainRequest{ Domains: domains, Bundle: true, - MustStaple: ocspMustStaple, PreferredChain: p.PreferredChain, } @@ -630,7 +626,6 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, request := certificate.ObtainRequest{ Domains: domains, Bundle: true, - MustStaple: ocspMustStaple, PreferredChain: p.PreferredChain, } @@ -821,11 +816,18 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat logger.Info().Msgf("Renewing certificate from LE : %+v", cert.Domain) - renewedCert, err := client.Certificate.Renew(certificate.Resource{ + res := certificate.Resource{ Domain: cert.Domain.Main, PrivateKey: cert.Key, Certificate: cert.Certificate.Certificate, - }, true, ocspMustStaple, p.PreferredChain) + } + + opts := &certificate.RenewOptions{ + Bundle: true, + PreferredChain: p.PreferredChain, + } + + renewedCert, err := client.Certificate.RenewWithOptions(res, opts) if err != nil { logger.Error().Err(err).Msgf("Error renewing certificate from LE: %v", cert.Domain) continue diff --git a/pkg/provider/configuration.go b/pkg/provider/configuration.go index 325afabc1..4cfcc1c5c 100644 --- a/pkg/provider/configuration.go +++ b/pkg/provider/configuration.go @@ -13,9 +13,10 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/tls" ) -// Merge Merges multiple configurations. +// Merge merges multiple configurations. func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration) *dynamic.Configuration { logger := log.Ctx(ctx) @@ -36,6 +37,9 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration Routers: make(map[string]*dynamic.UDPRouter), Services: make(map[string]*dynamic.UDPService), }, + TLS: &dynamic.TLSConfiguration{ + Stores: make(map[string]tls.Store), + }, } servicesToDelete := map[string]struct{}{} @@ -68,6 +72,9 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration transportsTCPToDelete := map[string]struct{}{} transportsTCP := map[string][]string{} + storesToDelete := map[string]struct{}{} + stores := map[string][]string{} + var sortedKeys []string for key := range configurations { sortedKeys = append(sortedKeys, key) @@ -145,6 +152,13 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration middlewaresTCPToDelete[middlewareName] = struct{}{} } } + + for storeName, store := range conf.TLS.Stores { + stores[storeName] = append(stores[storeName], root) + if !AddStore(configuration.TLS, storeName, store) { + storesToDelete[storeName] = struct{}{} + } + } } for serviceName := range servicesToDelete { @@ -217,6 +231,12 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration delete(configuration.TCP.Middlewares, middlewareName) } + for storeName := range storesToDelete { + logger.Error().Str("storeName", storeName). + Msgf("TLS store defined multiple times with different configurations in %v", stores[storeName]) + delete(configuration.TLS.Stores, storeName) + } + return configuration } @@ -365,7 +385,17 @@ func AddMiddleware(configuration *dynamic.HTTPConfiguration, middlewareName stri return reflect.DeepEqual(configuration.Middlewares[middlewareName], middleware) } -// MakeDefaultRuleTemplate Creates the default rule template. +// AddStore adds a middleware to a configurations. +func AddStore(configuration *dynamic.TLSConfiguration, storeName string, store tls.Store) bool { + if _, ok := configuration.Stores[storeName]; !ok { + configuration.Stores[storeName] = store + return true + } + + return reflect.DeepEqual(configuration.Stores[storeName], store) +} + +// MakeDefaultRuleTemplate creates the default rule template. func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) { defaultFuncMap := sprig.TxtFuncMap() defaultFuncMap["normalize"] = Normalize @@ -377,7 +407,7 @@ func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*tem return template.New("defaultRule").Funcs(defaultFuncMap).Parse(defaultRule) } -// BuildTCPRouterConfiguration Builds a router configuration. +// BuildTCPRouterConfiguration builds a router configuration. func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCPConfiguration) { for routerName, router := range configuration.Routers { loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger() @@ -403,7 +433,7 @@ func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCP } } -// BuildUDPRouterConfiguration Builds a router configuration. +// BuildUDPRouterConfiguration builds a router configuration. func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDPConfiguration) { for routerName, router := range configuration.Routers { loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger() @@ -426,7 +456,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP } } -// BuildRouterConfiguration Builds a router configuration. +// BuildRouterConfiguration builds a router configuration. func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPConfiguration, defaultRouterName string, defaultRuleTpl *template.Template, model interface{}) { if len(configuration.Routers) == 0 { if len(configuration.Services) > 1 { @@ -474,7 +504,7 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo } } -// Normalize Replace all special chars with `-`. +// Normalize replaces all special chars with `-`. func Normalize(name string) string { fargs := func(c rune) bool { return !unicode.IsLetter(c) && !unicode.IsNumber(c) diff --git a/pkg/provider/constraints/constraints_tags.go b/pkg/provider/constraints/constraints_tags.go index cb29997a3..b61f43c5d 100644 --- a/pkg/provider/constraints/constraints_tags.go +++ b/pkg/provider/constraints/constraints_tags.go @@ -3,6 +3,7 @@ package constraints import ( "errors" "regexp" + "slices" "github.com/vulcand/predicate" ) @@ -47,12 +48,7 @@ func MatchTags(tags []string, expr string) (bool, error) { func tagFn(name string) constraintTagFunc { return func(tags []string) bool { - for _, tag := range tags { - if tag == name { - return true - } - } - return false + return slices.Contains(tags, name) } } @@ -63,13 +59,9 @@ func tagRegexFn(expr string) constraintTagFunc { return false } - for _, tag := range tags { - if exp.MatchString(tag) { - return true - } - } - - return false + return slices.ContainsFunc(tags, func(tag string) bool { + return exp.MatchString(tag) + }) } } diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 4649eb3bd..56568c29a 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -76,6 +76,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -131,6 +134,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -178,6 +184,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -225,6 +234,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -278,6 +290,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, } @@ -299,7 +314,7 @@ func TestDefaultRule(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.items); i++ { + for i := range len(test.items) { var err error test.items[i].ExtraConf, err = p.getExtraConf(test.items[i].Labels) require.NoError(t, err) @@ -370,6 +385,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -444,6 +462,9 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -535,6 +556,9 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -614,6 +638,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -678,6 +705,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -739,6 +769,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -803,6 +836,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -856,6 +892,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -910,6 +949,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -962,6 +1004,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1015,6 +1060,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1077,6 +1125,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1125,6 +1176,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1184,6 +1238,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1250,6 +1307,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1309,6 +1369,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1380,6 +1443,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1445,6 +1511,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1526,6 +1595,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1587,6 +1659,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1662,6 +1737,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1729,6 +1807,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1782,6 +1863,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1836,6 +1920,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1897,6 +1984,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1928,6 +2018,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1960,6 +2053,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1992,6 +2088,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2024,6 +2123,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2058,6 +2160,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2112,6 +2217,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2176,6 +2284,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2233,6 +2344,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2307,6 +2421,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2355,6 +2472,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2398,6 +2518,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2451,6 +2574,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2500,6 +2626,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2591,6 +2720,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2679,6 +2811,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2722,6 +2857,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2765,6 +2903,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2809,6 +2950,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2915,6 +3059,9 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2990,6 +3137,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3065,6 +3215,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3117,6 +3270,78 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "one container with default generated certificate labels", + items: []itemData{ + { + ID: "Test", + Node: "Node1", + Name: "dev/Test", + Labels: map[string]string{ + "traefik.tls.stores.default.defaultgeneratedcert.resolver": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.main": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "dev-Test": { + Service: "dev-Test", + Rule: "Host(`dev-Test.traefik.wtf`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "dev-Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, }, }, } @@ -3141,7 +3366,7 @@ func Test_buildConfiguration(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.items); i++ { + for i := range len(test.items) { var err error test.items[i].ExtraConf, err = p.getExtraConf(test.items[i].Labels) require.NoError(t, err) @@ -3297,6 +3522,9 @@ func TestFilterHealthStatuses(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3331,6 +3559,9 @@ func TestFilterHealthStatuses(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3394,6 +3625,9 @@ func TestFilterHealthStatuses(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3484,6 +3718,9 @@ func TestFilterHealthStatuses(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3619,6 +3856,9 @@ func TestFilterHealthStatuses(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, } diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index d2c40851a..fb198053c 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/types" ) func TestDynConfBuilder_DefaultRule(t *testing.T) { @@ -80,6 +82,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -140,6 +145,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -202,6 +210,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -256,6 +267,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -310,6 +324,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -370,6 +387,9 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, } @@ -389,7 +409,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.containers); i++ { + for i := range len(test.containers) { var err error test.containers[i].ExtraConf, err = p.extractLabels(test.containers[i]) require.NoError(t, err) @@ -452,6 +472,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -493,6 +516,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -534,6 +560,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -593,6 +622,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -686,6 +718,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -766,6 +801,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -827,6 +865,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -889,6 +930,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -949,6 +993,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1010,6 +1057,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1080,6 +1130,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1141,6 +1194,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1208,6 +1264,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1294,6 +1353,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1378,6 +1440,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1445,6 +1510,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1535,6 +1603,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1619,6 +1690,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1725,6 +1799,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1803,6 +1880,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1903,6 +1983,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1986,6 +2069,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2072,6 +2158,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2133,6 +2222,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2195,6 +2287,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2264,6 +2359,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2301,6 +2399,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2340,6 +2441,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2381,6 +2485,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2409,6 +2516,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2453,6 +2563,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2484,6 +2597,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2525,6 +2641,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2556,6 +2675,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2596,6 +2718,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2638,6 +2763,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2700,6 +2828,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2772,6 +2903,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2837,6 +2971,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2895,6 +3032,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2951,6 +3091,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3002,6 +3145,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3063,6 +3209,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3120,6 +3269,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3225,6 +3377,9 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3276,6 +3431,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3328,6 +3486,9 @@ func TestDynConfBuilder_build(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3397,6 +3558,92 @@ func TestDynConfBuilder_build(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "one container with default generated certificate labels", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tls.stores.default.defaultgeneratedcert.resolver": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.main": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar", + }, + NetworkSettings: networkSettings{ + Ports: nat.PortMap{ + nat.Port("79/tcp"): []nat.PortBinding{{ + HostIP: "192.168.0.1", + HostPort: "8080", + }}, + nat.Port("80/tcp"): []nat.PortBinding{{ + HostIP: "192.168.0.1", + HostPort: "8081", + }}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:79", + }, + }, + PassHostHeader: Bool(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, }, }, } @@ -3420,7 +3667,7 @@ func TestDynConfBuilder_build(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.containers); i++ { + for i := range len(test.containers) { var err error test.containers[i].ExtraConf, err = p.extractLabels(test.containers[i]) require.NoError(t, err) diff --git a/pkg/provider/docker/data.go b/pkg/provider/docker/data.go index a4af51d82..509f2431d 100644 --- a/pkg/provider/docker/data.go +++ b/pkg/provider/docker/data.go @@ -25,7 +25,7 @@ type networkSettings struct { Networks map[string]*networkData } -// Network holds the network data to the provider. +// networkData holds the network data to the provider. type networkData struct { Name string Addr string diff --git a/pkg/provider/ecs/config_test.go b/pkg/provider/ecs/config_test.go index c840c5dad..a20dc3542 100644 --- a/pkg/provider/ecs/config_test.go +++ b/pkg/provider/ecs/config_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/types" ) func Int(v int) *int { return &v } @@ -76,6 +78,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -131,6 +136,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -188,6 +196,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -237,6 +248,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -286,6 +300,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -341,6 +358,9 @@ func TestDefaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, } @@ -359,7 +379,7 @@ func TestDefaultRule(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.instances); i++ { + for i := range len(test.instances) { var err error test.instances[i].ExtraConf, err = p.getConfiguration(test.instances[i]) require.NoError(t, err) @@ -413,6 +433,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -449,6 +472,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -485,6 +511,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -539,6 +568,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -622,6 +654,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -692,6 +727,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -748,6 +786,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -805,6 +846,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -860,6 +904,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -916,6 +963,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -981,6 +1031,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1037,6 +1090,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1094,6 +1150,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1165,6 +1224,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1239,6 +1301,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1301,6 +1366,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1381,6 +1449,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1455,6 +1526,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1546,6 +1620,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1614,6 +1691,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1699,6 +1779,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1773,6 +1856,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1849,6 +1935,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1905,6 +1994,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1962,6 +2054,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2019,6 +2114,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2097,6 +2195,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2161,6 +2262,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2193,6 +2297,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2227,6 +2334,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2263,6 +2373,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2300,6 +2413,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2336,6 +2452,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2373,6 +2492,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2430,6 +2552,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2497,6 +2622,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2557,6 +2685,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2610,6 +2741,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2661,6 +2795,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2707,6 +2844,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2763,6 +2903,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2815,6 +2958,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2910,6 +3056,9 @@ func Test_buildConfiguration(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2956,6 +3105,9 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -3003,6 +3155,80 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "one container with default generated certificate", + containers: []ecsInstance{ + instance( + name("Test"), + labels(map[string]string{ + "traefik.tls.stores.default.defaultgeneratedcert.resolver": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.main": "foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar", + }), + iMachine( + mState(ec2.InstanceStateNameRunning), + mPrivateIP("127.0.0.1"), + mPorts( + mPort(0, 80, "tcp"), + ), + ), + ), + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, }, }, } @@ -3022,7 +3248,7 @@ func Test_buildConfiguration(t *testing.T) { err := p.Init() require.NoError(t, err) - for i := 0; i < len(test.containers); i++ { + for i := range len(test.containers) { var err error test.containers[i].ExtraConf, err = p.getConfiguration(test.containers[i]) require.NoError(t, err) diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go index 93337c766..2280f3605 100644 --- a/pkg/provider/ecs/ecs.go +++ b/pkg/provider/ecs/ecs.go @@ -402,7 +402,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI } func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]*ecs.Task) (map[string]*ssm.InstanceInformation, error) { - instanceIds := make(map[string]string) + instanceIDs := make(map[string]string) miInstances := make(map[string]*ssm.InstanceInformation) var containerInstancesArns []*string @@ -424,7 +424,7 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu } for _, container := range resp.ContainerInstances { - instanceIds[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) + instanceIDs[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) // Disallow EC2 Instance IDs // This prevents considering EC2 instances in ECS @@ -452,7 +452,7 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu if len(page.InstanceInformationList) > 0 { for _, i := range page.InstanceInformationList { if i.InstanceId != nil { - miInstances[instanceIds[aws.StringValue(i.InstanceId)]] = i + miInstances[instanceIDs[aws.StringValue(i.InstanceId)]] = i } } } @@ -468,7 +468,7 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu } func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]*ecs.Task) (map[string]*ec2.Instance, error) { - instanceIds := make(map[string]string) + instanceIDs := make(map[string]string) ec2Instances := make(map[string]*ec2.Instance) var containerInstancesArns []*string @@ -490,7 +490,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl } for _, container := range resp.ContainerInstances { - instanceIds[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) + instanceIDs[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) // Disallow Instance IDs of the form mi-* // This prevents considering external instances in ECS Anywhere setups // and getting InvalidInstanceID.Malformed error when calling the describe-instances endpoint. @@ -513,7 +513,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl for _, r := range page.Reservations { for _, i := range r.Instances { if i.InstanceId != nil { - ec2Instances[instanceIds[aws.StringValue(i.InstanceId)]] = i + ec2Instances[instanceIDs[aws.StringValue(i.InstanceId)]] = i } } } diff --git a/pkg/provider/ecs/ecs_test.go b/pkg/provider/ecs/ecs_test.go index f55510305..ede115a41 100644 --- a/pkg/provider/ecs/ecs_test.go +++ b/pkg/provider/ecs/ecs_test.go @@ -73,7 +73,7 @@ func TestChunkIDs(t *testing.T) { t.Parallel() var IDs []*string - for v := 0; v < test.count; v++ { + for range test.count { IDs = append(IDs, aws.String("a")) } diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go index 66c325ebe..27bf10048 100644 --- a/pkg/provider/kubernetes/crd/client.go +++ b/pkg/provider/kubernetes/crd/client.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "time" "github.com/rs/zerolog/log" @@ -490,12 +491,8 @@ func (c *clientWrapper) isWatchedNamespace(ns string) bool { if c.isNamespaceAll { return true } - for _, watchedNamespace := range c.watchedNamespaces { - if watchedNamespace == ns { - return true - } - } - return false + + return slices.Contains(c.watchedNamespaces, ns) } // translateNotFoundError will translate a "not found" error to a boolean return diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 6caddca3a..ae1c39efa 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -73,7 +73,7 @@ func (p *Provider) applyRouterTransform(ctx context.Context, rt *dynamic.Router, return } - err := p.routerTransform.Apply(ctx, rt, ingress.Annotations) + err := p.routerTransform.Apply(ctx, rt, ingress) if err != nil { log.Ctx(ctx).Error().Err(err).Msg("Apply router transform") } diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index dc957e966..9bf57a5f8 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "slices" "time" "github.com/rs/zerolog/log" @@ -567,10 +568,6 @@ func (c *clientWrapper) isWatchedNamespace(ns string) bool { if c.isNamespaceAll { return true } - for _, watchedNamespace := range c.watchedNamespaces { - if watchedNamespace == ns { - return true - } - } - return false + + return slices.Contains(c.watchedNamespaces, ns) } diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 724d5b841..56cb4f952 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -73,7 +73,7 @@ func (p *Provider) applyRouterTransform(ctx context.Context, rt *dynamic.Router, return } - err := p.routerTransform.Apply(ctx, rt, route.Annotations) + err := p.routerTransform.Apply(ctx, rt, route) if err != nil { log.Ctx(ctx).Error().Err(err).Msg("Apply router transform") } diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go index cccc3ce95..d7959d571 100644 --- a/pkg/provider/kubernetes/ingress/client.go +++ b/pkg/provider/kubernetes/ingress/client.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "time" "github.com/hashicorp/go-version" @@ -415,12 +416,8 @@ func (c *clientWrapper) isWatchedNamespace(ns string) bool { if c.isNamespaceAll { return true } - for _, watchedNamespace := range c.watchedNamespaces { - if watchedNamespace == ns { - return true - } - } - return false + + return slices.Contains(c.watchedNamespaces, ns) } // filterIngressClassByName return a slice containing ingressclasses with the correct name. diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 4d50749f4..80a2cdeb3 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -9,6 +9,7 @@ import ( "net" "os" "regexp" + "slices" "sort" "strconv" "strings" @@ -66,7 +67,7 @@ func (p *Provider) applyRouterTransform(ctx context.Context, rt *dynamic.Router, return } - err := p.routerTransform.Apply(ctx, rt, ingress.Annotations) + err := p.routerTransform.Apply(ctx, rt, ingress) if err != nil { log.Ctx(ctx).Error().Err(err).Msg("Apply router transform") } @@ -418,13 +419,9 @@ func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client) err func (p *Provider) shouldProcessIngress(ingress *netv1.Ingress, ingressClasses []*netv1.IngressClass) bool { // configuration through the new kubernetes ingressClass if ingress.Spec.IngressClassName != nil { - for _, ic := range ingressClasses { - if *ingress.Spec.IngressClassName == ic.ObjectMeta.Name { - return true - } - } - - return false + return slices.ContainsFunc(ingressClasses, func(ic *netv1.IngressClass) bool { + return *ingress.Spec.IngressClassName == ic.ObjectMeta.Name + }) } return p.IngressClass == ingress.Annotations[annotationKubernetesIngressClass] || diff --git a/pkg/provider/kubernetes/k8s/router_transform.go b/pkg/provider/kubernetes/k8s/router_transform.go index ff1b2e1a1..18a410282 100644 --- a/pkg/provider/kubernetes/k8s/router_transform.go +++ b/pkg/provider/kubernetes/k8s/router_transform.go @@ -4,8 +4,9 @@ import ( "context" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "k8s.io/apimachinery/pkg/runtime" ) type RouterTransform interface { - Apply(ctx context.Context, rt *dynamic.Router, annotations map[string]string) error + Apply(ctx context.Context, rt *dynamic.Router, object runtime.Object) error } diff --git a/pkg/provider/nomad/config_test.go b/pkg/provider/nomad/config_test.go index 1064967d0..ef13dee71 100644 --- a/pkg/provider/nomad/config_test.go +++ b/pkg/provider/nomad/config_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/types" ) func Test_defaultRule(t *testing.T) { @@ -68,6 +70,9 @@ func Test_defaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -123,6 +128,9 @@ func Test_defaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -169,6 +177,9 @@ func Test_defaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -221,6 +232,9 @@ func Test_defaultRule(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, } @@ -296,6 +310,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -373,6 +390,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -437,6 +457,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -498,6 +521,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -562,6 +588,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -615,6 +644,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -669,6 +701,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -721,6 +756,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -774,6 +812,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -836,6 +877,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -885,6 +929,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -944,6 +991,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1010,6 +1060,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1069,6 +1122,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1141,6 +1197,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1207,6 +1266,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1267,6 +1329,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1332,6 +1397,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1385,6 +1453,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1439,6 +1510,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1500,6 +1574,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1531,6 +1608,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1563,6 +1643,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1596,6 +1679,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1630,6 +1716,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1684,6 +1773,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1748,6 +1840,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1805,6 +1900,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1855,6 +1953,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1903,6 +2004,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1946,6 +2050,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -1999,6 +2106,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2048,6 +2158,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2139,6 +2252,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2227,6 +2343,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2270,6 +2389,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2313,6 +2435,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2357,6 +2482,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2445,6 +2573,9 @@ func Test_buildConfig(t *testing.T) { }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2522,6 +2653,9 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, }, }, { @@ -2599,6 +2733,78 @@ func Test_buildConfig(t *testing.T) { Services: map[string]*dynamic.Service{}, ServersTransports: map[string]*dynamic.ServersTransport{}, }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "one service with default generated certificate tags", + items: []item{ + { + ID: "id", + Node: "Node1", + Name: "dev/Test", + Address: "127.0.0.1", + Port: 9999, + ExtraConf: configuration{Enable: true}, + Tags: []string{ + "traefik.tls.stores.default.defaultgeneratedcert.resolver = foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.main = foobar", + "traefik.tls.stores.default.defaultgeneratedcert.domain.sans = foobar, fiibar", + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "dev-Test": { + Service: "dev-Test", + Rule: "Host(`dev-Test.traefik.test`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "dev-Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:9999", + }, + }, + PassHostHeader: Bool(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{ + "default": { + DefaultGeneratedCert: &tls.GeneratedCert{ + Resolver: "foobar", + Domain: &types.Domain{ + Main: "foobar", + SANs: []string{"foobar", "fiibar"}, + }, + }, + }, + }, + }, }, }, } diff --git a/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json b/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json index 1dec58871..38a75d6b3 100644 --- a/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json +++ b/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json @@ -7,7 +7,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646 + "priority": 9223372036854775806 }, "dashboard": { "entryPoints": [ @@ -19,7 +19,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645 + "priority": 9223372036854775805 } }, "services": { @@ -47,4 +47,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/fixtures/api_insecure_without_dashboard.json b/pkg/provider/traefik/fixtures/api_insecure_without_dashboard.json index 994fcf3af..9a98c0a1d 100644 --- a/pkg/provider/traefik/fixtures/api_insecure_without_dashboard.json +++ b/pkg/provider/traefik/fixtures/api_insecure_without_dashboard.json @@ -7,7 +7,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646 + "priority": 9223372036854775806 } }, "services": { @@ -17,4 +17,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/fixtures/full_configuration.json b/pkg/provider/traefik/fixtures/full_configuration.json index 6e9f2d4b3..ed0ebfba6 100644 --- a/pkg/provider/traefik/fixtures/full_configuration.json +++ b/pkg/provider/traefik/fixtures/full_configuration.json @@ -7,7 +7,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/api`)", - "priority": 2147483646 + "priority": 9223372036854775806 }, "dashboard": { "entryPoints": [ @@ -19,7 +19,7 @@ ], "service": "dashboard@internal", "rule": "PathPrefix(`/`)", - "priority": 2147483645 + "priority": 9223372036854775805 }, "debug": { "entryPoints": [ @@ -27,7 +27,7 @@ ], "service": "api@internal", "rule": "PathPrefix(`/debug`)", - "priority": 2147483646 + "priority": 9223372036854775806 }, "ping": { "entryPoints": [ @@ -35,7 +35,7 @@ ], "service": "ping@internal", "rule": "PathPrefix(`/ping`)", - "priority": 2147483647 + "priority": 9223372036854775807 }, "prometheus": { "entryPoints": [ @@ -43,7 +43,7 @@ ], "service": "prometheus@internal", "rule": "PathPrefix(`/metrics`)", - "priority": 2147483647 + "priority": 9223372036854775807 }, "rest": { "entryPoints": [ @@ -51,7 +51,7 @@ ], "service": "rest@internal", "rule": "PathPrefix(`/api/providers`)", - "priority": 2147483647 + "priority": 9223372036854775807 } }, "services": { @@ -82,4 +82,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/fixtures/ping_simple.json b/pkg/provider/traefik/fixtures/ping_simple.json index 3ee3b4050..6f159dce5 100644 --- a/pkg/provider/traefik/fixtures/ping_simple.json +++ b/pkg/provider/traefik/fixtures/ping_simple.json @@ -7,7 +7,7 @@ ], "service": "ping@internal", "rule": "PathPrefix(`/ping`)", - "priority": 2147483647 + "priority": 9223372036854775807 } }, "services": { @@ -17,4 +17,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/fixtures/prometheus_simple.json b/pkg/provider/traefik/fixtures/prometheus_simple.json index 3c856804f..b636b4569 100644 --- a/pkg/provider/traefik/fixtures/prometheus_simple.json +++ b/pkg/provider/traefik/fixtures/prometheus_simple.json @@ -7,7 +7,7 @@ ], "service": "prometheus@internal", "rule": "PathPrefix(`/metrics`)", - "priority": 2147483647 + "priority": 9223372036854775807 } }, "services": { @@ -17,4 +17,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/fixtures/rest_insecure.json b/pkg/provider/traefik/fixtures/rest_insecure.json index 7952359f3..e11a84e77 100644 --- a/pkg/provider/traefik/fixtures/rest_insecure.json +++ b/pkg/provider/traefik/fixtures/rest_insecure.json @@ -7,7 +7,7 @@ ], "service": "rest@internal", "rule": "PathPrefix(`/api/providers`)", - "priority": 2147483647 + "priority": 9223372036854775807 } }, "services": { @@ -17,4 +17,4 @@ }, "tcp": {}, "tls": {} -} \ No newline at end of file +} diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go index 9a4105623..7a78703d0 100644 --- a/pkg/provider/traefik/internal.go +++ b/pkg/provider/traefik/internal.go @@ -108,7 +108,7 @@ func (i *Provider) acme(cfg *dynamic.Configuration) { Rule: "PathPrefix(`/.well-known/acme-challenge/`)", EntryPoints: eps, Service: "acme-http@internal", - Priority: math.MaxInt32, + Priority: math.MaxInt, } cfg.HTTP.Routers["acme-http"] = rt @@ -239,7 +239,7 @@ func (i *Provider) apiConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["api"] = &dynamic.Router{ EntryPoints: []string{defaultInternalEntryPointName}, Service: "api@internal", - Priority: math.MaxInt32 - 1, + Priority: math.MaxInt - 1, Rule: "PathPrefix(`/api`)", } @@ -247,7 +247,7 @@ func (i *Provider) apiConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["dashboard"] = &dynamic.Router{ EntryPoints: []string{defaultInternalEntryPointName}, Service: "dashboard@internal", - Priority: math.MaxInt32 - 2, + Priority: math.MaxInt - 2, Rule: "PathPrefix(`/`)", Middlewares: []string{"dashboard_redirect@internal", "dashboard_stripprefix@internal"}, } @@ -268,7 +268,7 @@ func (i *Provider) apiConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["debug"] = &dynamic.Router{ EntryPoints: []string{defaultInternalEntryPointName}, Service: "api@internal", - Priority: math.MaxInt32 - 1, + Priority: math.MaxInt - 1, Rule: "PathPrefix(`/debug`)", } } @@ -290,7 +290,7 @@ func (i *Provider) pingConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["ping"] = &dynamic.Router{ EntryPoints: []string{i.staticCfg.Ping.EntryPoint}, Service: "ping@internal", - Priority: math.MaxInt32, + Priority: math.MaxInt, Rule: "PathPrefix(`/ping`)", } } @@ -307,7 +307,7 @@ func (i *Provider) restConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["rest"] = &dynamic.Router{ EntryPoints: []string{defaultInternalEntryPointName}, Service: "rest@internal", - Priority: math.MaxInt32, + Priority: math.MaxInt, Rule: "PathPrefix(`/api/providers`)", } } @@ -324,7 +324,7 @@ func (i *Provider) prometheusConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Routers["prometheus"] = &dynamic.Router{ EntryPoints: []string{i.staticCfg.Metrics.Prometheus.EntryPoint}, Service: "prometheus@internal", - Priority: math.MaxInt32, + Priority: math.MaxInt, Rule: "PathPrefix(`/metrics`)", } } diff --git a/pkg/redactor/redactor.go b/pkg/redactor/redactor.go index 6761d9eb9..c28feaa1a 100644 --- a/pkg/redactor/redactor.go +++ b/pkg/redactor/redactor.go @@ -83,7 +83,7 @@ func doOnStruct(field reflect.Value, tag string, redactByDefault bool) error { } } case reflect.Struct: - for i := 0; i < field.NumField(); i++ { + for i := range field.NumField() { fld := field.Field(i) stField := field.Type().Field(i) if !isExported(stField) { @@ -138,7 +138,7 @@ func doOnStruct(field reflect.Value, tag string, redactByDefault bool) error { } } case reflect.Slice: - for j := 0; j < field.Len(); j++ { + for j := range field.Len() { if err := doOnStruct(field.Index(j), tag, redactByDefault); err != nil { return err } @@ -179,7 +179,7 @@ func reset(field reflect.Value, name string) error { switch field.Type().Elem().Kind() { case reflect.String: slice := reflect.MakeSlice(field.Type(), field.Len(), field.Len()) - for j := 0; j < field.Len(); j++ { + for j := range field.Len() { slice.Index(j).SetString(maskShort) } field.Set(slice) diff --git a/pkg/server/aggregator.go b/pkg/server/aggregator.go index c6a88e590..75ad759cf 100644 --- a/pkg/server/aggregator.go +++ b/pkg/server/aggregator.go @@ -1,6 +1,8 @@ package server import ( + "slices" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" @@ -98,7 +100,7 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint if configuration.TLS != nil { for _, cert := range configuration.TLS.Certificates { - if containsACMETLS1(cert.Stores) && pvd != "tlsalpn.acme" { + if slices.Contains(cert.Stores, tlsalpn01.ACMETLS1Protocol) && pvd != "tlsalpn.acme" { continue } @@ -127,14 +129,14 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint } if len(defaultTLSStoreProviders) > 1 { - log.Error().Msgf("Default TLS Stores defined multiple times in %v", defaultTLSOptionProviders) + log.Error().Msgf("Default TLS Store defined in multiple providers: %v", defaultTLSStoreProviders) delete(conf.TLS.Stores, tls.DefaultTLSStoreName) } if len(defaultTLSOptionProviders) == 0 { conf.TLS.Options[tls.DefaultTLSConfigName] = tls.DefaultTLSOptions } else if len(defaultTLSOptionProviders) > 1 { - log.Error().Msgf("Default TLS Options defined multiple times in %v", defaultTLSOptionProviders) + log.Error().Msgf("Default TLS Options defined in multiple providers %v", defaultTLSOptionProviders) // We do not set an empty tls.TLS{} as above so that we actually get a "cascading failure" later on, // i.e. routers depending on this missing TLS option will fail to initialize as well. delete(conf.TLS.Options, tls.DefaultTLSConfigName) @@ -212,13 +214,3 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { return cfg } - -func containsACMETLS1(stores []string) bool { - for _, store := range stores { - if store == tlsalpn01.ACMETLS1Protocol { - return true - } - } - - return false -} diff --git a/pkg/server/configurationwatcher_test.go b/pkg/server/configurationwatcher_test.go index d77874634..53143c5bc 100644 --- a/pkg/server/configurationwatcher_test.go +++ b/pkg/server/configurationwatcher_test.go @@ -311,7 +311,7 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) { throttleDuration: 30 * time.Millisecond, } - for i := 0; i < 5; i++ { + for i := range 5 { pvd.messages = append(pvd.messages, dynamic.Message{ ProviderName: "mock", Configuration: &dynamic.Configuration{ diff --git a/pkg/server/middleware/middlewares.go b/pkg/server/middleware/middlewares.go index 3065b16ae..0955fbcae 100644 --- a/pkg/server/middleware/middlewares.go +++ b/pkg/server/middleware/middlewares.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "reflect" + "slices" "strings" "github.com/containous/alice" @@ -24,6 +25,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/inflightreq" "github.com/traefik/traefik/v3/pkg/middlewares/ipallowlist" "github.com/traefik/traefik/v3/pkg/middlewares/ipwhitelist" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/passtlsclientcert" "github.com/traefik/traefik/v3/pkg/middlewares/ratelimiter" "github.com/traefik/traefik/v3/pkg/middlewares/redirect" @@ -32,7 +34,6 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/retry" "github.com/traefik/traefik/v3/pkg/middlewares/stripprefix" "github.com/traefik/traefik/v3/pkg/middlewares/stripprefixregex" - "github.com/traefik/traefik/v3/pkg/middlewares/tracing" "github.com/traefik/traefik/v3/pkg/server/provider" ) @@ -99,7 +100,7 @@ func checkRecursion(ctx context.Context, middlewareName string) (context.Context if !ok { currentStack = []string{} } - if inSlice(middlewareName, currentStack) { + if slices.Contains(currentStack, middlewareName) { return ctx, fmt.Errorf("could not instantiate middleware %s: recursion detected in %s", middlewareName, strings.Join(append(currentStack, middlewareName), "->")) } return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil @@ -390,14 +391,5 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) ( // The tracing middleware is a NOOP if tracing is not setup on the middleware chain. // Hence, regarding internal resources' observability deactivation, // this would not enable tracing. - return tracing.WrapMiddleware(ctx, middleware), nil -} - -func inSlice(element string, stack []string) bool { - for _, value := range stack { - if value == element { - return true - } - } - return false + return observability.WrapMiddleware(ctx, middleware), nil } diff --git a/pkg/server/middleware/observability.go b/pkg/server/middleware/observability.go index af65cf480..cf8386999 100644 --- a/pkg/server/middleware/observability.go +++ b/pkg/server/middleware/observability.go @@ -14,8 +14,8 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/capture" metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics" - tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing" - "go.opentelemetry.io/otel/trace" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "github.com/traefik/traefik/v3/pkg/tracing" ) // ObservabilityMgr is a manager for observability (AccessLogs, Metrics and Tracing) enablement. @@ -23,15 +23,17 @@ type ObservabilityMgr struct { config static.Configuration accessLoggerMiddleware *accesslog.Handler metricsRegistry metrics.Registry - tracer trace.Tracer + semConvMetricRegistry *metrics.SemConvMetricsRegistry + tracer *tracing.Tracer tracerCloser io.Closer } // NewObservabilityMgr creates a new ObservabilityMgr. -func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer trace.Tracer, tracerCloser io.Closer) *ObservabilityMgr { +func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Registry, semConvMetricRegistry *metrics.SemConvMetricsRegistry, accessLoggerMiddleware *accesslog.Handler, tracer *tracing.Tracer, tracerCloser io.Closer) *ObservabilityMgr { return &ObservabilityMgr{ config: config, metricsRegistry: metricsRegistry, + semConvMetricRegistry: semConvMetricRegistry, accessLoggerMiddleware: accessLoggerMiddleware, tracer: tracer, tracerCloser: tracerCloser, @@ -39,35 +41,35 @@ func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Re } // BuildEPChain an observability middleware chain by entry point. -func (c *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string) alice.Chain { +func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string) alice.Chain { chain := alice.New() - if c == nil { + if o == nil { return chain } - if c.accessLoggerMiddleware != nil || c.metricsRegistry != nil && (c.metricsRegistry.IsEpEnabled() || c.metricsRegistry.IsRouterEnabled() || c.metricsRegistry.IsSvcEnabled()) { - if c.ShouldAddAccessLogs(resourceName) || c.ShouldAddMetrics(resourceName) { + if o.accessLoggerMiddleware != nil || o.metricsRegistry != nil && (o.metricsRegistry.IsEpEnabled() || o.metricsRegistry.IsRouterEnabled() || o.metricsRegistry.IsSvcEnabled()) { + if o.ShouldAddAccessLogs(resourceName) || o.ShouldAddMetrics(resourceName) { chain = chain.Append(capture.Wrap) } } - if c.accessLoggerMiddleware != nil && c.ShouldAddAccessLogs(resourceName) { - chain = chain.Append(accesslog.WrapHandler(c.accessLoggerMiddleware)) + if o.accessLoggerMiddleware != nil && o.ShouldAddAccessLogs(resourceName) { + chain = chain.Append(accesslog.WrapHandler(o.accessLoggerMiddleware)) chain = chain.Append(func(next http.Handler) (http.Handler, error) { return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil }) } - if c.tracer != nil && c.ShouldAddTracing(resourceName) { - chain = chain.Append(tracingMiddle.WrapEntryPointHandler(ctx, c.tracer, entryPointName)) + if (o.tracer != nil && o.ShouldAddTracing(resourceName)) || (o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName)) { + chain = chain.Append(observability.WrapEntryPointHandler(ctx, o.tracer, o.semConvMetricRegistry, entryPointName)) } - if c.metricsRegistry != nil && c.metricsRegistry.IsEpEnabled() && c.ShouldAddMetrics(resourceName) { - metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, c.metricsRegistry, entryPointName) + if o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName) { + metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, o.metricsRegistry, entryPointName) - if c.tracer != nil && c.ShouldAddTracing(resourceName) { - chain = chain.Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler)) + if o.tracer != nil && o.ShouldAddTracing(resourceName) { + chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) } else { chain = chain.Append(metricsHandler) } @@ -77,64 +79,73 @@ func (c *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName stri } // ShouldAddAccessLogs returns whether the access logs should be enabled for the given resource. -func (c *ObservabilityMgr) ShouldAddAccessLogs(resourceName string) bool { - if c == nil { +func (o *ObservabilityMgr) ShouldAddAccessLogs(resourceName string) bool { + if o == nil { return false } - return c.config.AccessLog != nil && (c.config.AccessLog.AddInternals || !strings.HasSuffix(resourceName, "@internal")) + return o.config.AccessLog != nil && (o.config.AccessLog.AddInternals || !strings.HasSuffix(resourceName, "@internal")) } // ShouldAddMetrics returns whether the metrics should be enabled for the given resource. -func (c *ObservabilityMgr) ShouldAddMetrics(resourceName string) bool { - if c == nil { +func (o *ObservabilityMgr) ShouldAddMetrics(resourceName string) bool { + if o == nil { return false } - return c.config.Metrics != nil && (c.config.Metrics.AddInternals || !strings.HasSuffix(resourceName, "@internal")) + return o.config.Metrics != nil && (o.config.Metrics.AddInternals || !strings.HasSuffix(resourceName, "@internal")) } // ShouldAddTracing returns whether the tracing should be enabled for the given resource. -func (c *ObservabilityMgr) ShouldAddTracing(resourceName string) bool { - if c == nil { +func (o *ObservabilityMgr) ShouldAddTracing(resourceName string) bool { + if o == nil { return false } - return c.config.Tracing != nil && (c.config.Tracing.AddInternals || !strings.HasSuffix(resourceName, "@internal")) + return o.config.Tracing != nil && (o.config.Tracing.AddInternals || !strings.HasSuffix(resourceName, "@internal")) } // MetricsRegistry is an accessor to the metrics registry. -func (c *ObservabilityMgr) MetricsRegistry() metrics.Registry { - if c == nil { +func (o *ObservabilityMgr) MetricsRegistry() metrics.Registry { + if o == nil { return nil } - return c.metricsRegistry + return o.metricsRegistry +} + +// SemConvMetricsRegistry is an accessor to the semantic conventions metrics registry. +func (o *ObservabilityMgr) SemConvMetricsRegistry() *metrics.SemConvMetricsRegistry { + if o == nil { + return nil + } + + return o.semConvMetricRegistry } // Close closes the accessLogger and tracer. -func (c *ObservabilityMgr) Close() { - if c == nil { +func (o *ObservabilityMgr) Close() { + if o == nil { return } - if c.accessLoggerMiddleware != nil { - if err := c.accessLoggerMiddleware.Close(); err != nil { + if o.accessLoggerMiddleware != nil { + if err := o.accessLoggerMiddleware.Close(); err != nil { log.Error().Err(err).Msg("Could not close the access log file") } } - if c.tracerCloser != nil { - if err := c.tracerCloser.Close(); err != nil { + if o.tracerCloser != nil { + if err := o.tracerCloser.Close(); err != nil { log.Error().Err(err).Msg("Could not close the tracer") } } } -func (c *ObservabilityMgr) RotateAccessLogs() error { - if c.accessLoggerMiddleware == nil { +func (o *ObservabilityMgr) RotateAccessLogs() error { + if o.accessLoggerMiddleware == nil { return nil } - return c.accessLoggerMiddleware.Rotate() + return o.accessLoggerMiddleware.Rotate() } diff --git a/pkg/server/middleware/tcp/middlewares.go b/pkg/server/middleware/tcp/middlewares.go index 0a5d30bc8..ddd6e0e2a 100644 --- a/pkg/server/middleware/tcp/middlewares.go +++ b/pkg/server/middleware/tcp/middlewares.go @@ -3,6 +3,7 @@ package tcpmiddleware import ( "context" "fmt" + "slices" "strings" "github.com/rs/zerolog/log" @@ -74,7 +75,7 @@ func checkRecursion(ctx context.Context, middlewareName string) (context.Context currentStack = []string{} } - if inSlice(middlewareName, currentStack) { + if slices.Contains(currentStack, middlewareName) { return ctx, fmt.Errorf("could not instantiate middleware %s: recursion detected in %s", middlewareName, strings.Join(append(currentStack, middlewareName), "->")) } @@ -118,12 +119,3 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) ( return middleware, nil } - -func inSlice(element string, stack []string) bool { - for _, value := range stack { - if value == element { - return true - } - } - return false -} diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index eae645383..369aaa7ab 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -13,8 +13,8 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/denyrouterrecursion" metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/recovery" - "github.com/traefik/traefik/v3/pkg/middlewares/tracing" httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" "github.com/traefik/traefik/v3/pkg/server/middleware" "github.com/traefik/traefik/v3/pkg/server/provider" @@ -221,11 +221,11 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn return chain.Extend(*mHandler).Then(sHandler) } - chain = chain.Append(tracing.WrapRouterHandler(ctx, routerName, router.Rule, provider.GetQualifiedName(ctx, router.Service))) + chain = chain.Append(observability.WrapRouterHandler(ctx, routerName, router.Rule, provider.GetQualifiedName(ctx, router.Service))) if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() { metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service)) - chain = chain.Append(tracing.WrapMiddleware(ctx, metricsHandler)) + chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) } if router.DefaultRule { diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index eb83ed25d..b52172cce 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -817,7 +817,7 @@ func BenchmarkRouterServe(b *testing.B) { reqHost := requestdecorator.New(nil) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for range b.N { reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP) } } @@ -852,7 +852,7 @@ func BenchmarkService(b *testing.B) { handler, _ := serviceManager.BuildHTTP(context.Background(), "foo-service") b.ReportAllocs() - for i := 0; i < b.N; i++ { + for range b.N { handler.ServeHTTP(w, req) } } diff --git a/pkg/server/routerfactory_test.go b/pkg/server/routerfactory_test.go index ebd923f62..971b58c99 100644 --- a/pkg/server/routerfactory_test.go +++ b/pkg/server/routerfactory_test.go @@ -193,7 +193,7 @@ func TestServerResponseEmptyBackend(t *testing.T) { dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) - observabiltyMgr := middleware.NewObservabilityMgr(staticConfig, nil, nil, nil, nil) + observabiltyMgr := middleware.NewObservabilityMgr(staticConfig, nil, nil, nil, nil, nil) factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, observabiltyMgr, nil, dialerManager) entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)})) diff --git a/pkg/server/server_entrypoint_tcp_test.go b/pkg/server/server_entrypoint_tcp_test.go index d53f160f8..9ec5d2686 100644 --- a/pkg/server/server_entrypoint_tcp_test.go +++ b/pkg/server/server_entrypoint_tcp_test.go @@ -112,7 +112,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) { // but technically also as early as the Shutdown has closed the listener, // i.e. during the shutdown and before the gracetime is over. var testOk bool - for i := 0; i < 10; i++ { + for range 10 { loopConn, err := net.Dial("tcp", epAddr) if err == nil { loopConn.Close() @@ -141,7 +141,7 @@ func startEntrypoint(entryPoint *TCPEntryPoint, router *tcprouter.Router) (net.C entryPoint.SwitchRouter(router) - for i := 0; i < 10; i++ { + for range 10 { conn, err := net.Dial("tcp", entryPoint.listener.Addr().String()) if err != nil { time.Sleep(100 * time.Millisecond) diff --git a/pkg/server/service/loadbalancer/mirror/mirror_test.go b/pkg/server/service/loadbalancer/mirror/mirror_test.go index c43d00b95..c48637006 100644 --- a/pkg/server/service/loadbalancer/mirror/mirror_test.go +++ b/pkg/server/service/loadbalancer/mirror/mirror_test.go @@ -32,7 +32,7 @@ func TestMirroringOn100(t *testing.T) { }), 50) assert.NoError(t, err) - for i := 0; i < 100; i++ { + for range 100 { mirror.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)) } @@ -61,7 +61,7 @@ func TestMirroringOn10(t *testing.T) { }), 50) assert.NoError(t, err) - for i := 0; i < 10; i++ { + for range 10 { mirror.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)) } @@ -156,7 +156,7 @@ func TestMirroringWithBody(t *testing.T) { mirror := New(handler, pool, defaultMaxBodySize, nil) - for i := 0; i < numMirrors; i++ { + for range numMirrors { err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assert.NotNil(t, r.Body) bb, err := io.ReadAll(r.Body) diff --git a/pkg/server/service/loadbalancer/wrr/wrr_test.go b/pkg/server/service/loadbalancer/wrr/wrr_test.go index 3708fa618..948e4df45 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr_test.go +++ b/pkg/server/service/loadbalancer/wrr/wrr_test.go @@ -24,7 +24,7 @@ func TestBalancer(t *testing.T) { }), Int(1)) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 4; i++ { + for range 4 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } @@ -52,7 +52,7 @@ func TestBalancerOneServerZeroWeight(t *testing.T) { balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0)) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 3; i++ { + for range 3 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } @@ -97,7 +97,7 @@ func TestBalancerOneServerDown(t *testing.T) { balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", false) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 3; i++ { + for range 3 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } @@ -119,14 +119,14 @@ func TestBalancerDownThenUp(t *testing.T) { balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", false) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 3; i++ { + for range 3 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } assert.Equal(t, 3, recorder.save["first"]) balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", true) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 2; i++ { + for range 2 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } assert.Equal(t, 1, recorder.save["first"]) @@ -168,7 +168,7 @@ func TestBalancerPropagate(t *testing.T) { }) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 8; i++ { + for range 8 { topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } assert.Equal(t, 2, recorder.save["first"]) @@ -181,7 +181,7 @@ func TestBalancerPropagate(t *testing.T) { // fourth gets downed, but balancer2 still up since third is still up. balancer2.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "fourth", false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 8; i++ { + for range 8 { topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } assert.Equal(t, 2, recorder.save["first"]) @@ -195,7 +195,7 @@ func TestBalancerPropagate(t *testing.T) { // down as well for topBalancer. balancer2.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "third", false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 8; i++ { + for range 8 { topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } assert.Equal(t, 4, recorder.save["first"]) @@ -246,7 +246,7 @@ func TestSticky(t *testing.T) { } req := httptest.NewRequest(http.MethodGet, "/", nil) - for i := 0; i < 3; i++ { + for range 3 { for _, cookie := range recorder.Result().Cookies() { assert.NotContains(t, "test=first", cookie.Value) assert.NotContains(t, "test=second", cookie.Value) @@ -284,7 +284,7 @@ func TestSticky_FallBack(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.AddCookie(&http.Cookie{Name: "test", Value: "second"}) - for i := 0; i < 3; i++ { + for range 3 { recorder.ResponseRecorder = httptest.NewRecorder() balancer.ServeHTTP(recorder, req) @@ -311,7 +311,7 @@ func TestBalancerBias(t *testing.T) { recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} - for i := 0; i < 14; i++ { + for range 14 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) } diff --git a/pkg/server/service/observability_roundtripper.go b/pkg/server/service/observability_roundtripper.go new file mode 100644 index 000000000..ae862deed --- /dev/null +++ b/pkg/server/service/observability_roundtripper.go @@ -0,0 +1,105 @@ +package service + +import ( + "context" + "fmt" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "github.com/traefik/traefik/v3/pkg/tracing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" +) + +type wrapper struct { + semConvMetricRegistry *metrics.SemConvMetricsRegistry + rt http.RoundTripper +} + +func (t *wrapper) RoundTrip(req *http.Request) (*http.Response, error) { + start := time.Now() + var span trace.Span + var tracingCtx context.Context + var tracer *tracing.Tracer + if tracer = tracing.TracerFromContext(req.Context()); tracer != nil { + tracingCtx, span = tracer.Start(req.Context(), "ReverseProxy", trace.WithSpanKind(trace.SpanKindClient)) + defer span.End() + + req = req.WithContext(tracingCtx) + + tracer.CaptureClientRequest(span, req) + tracing.InjectContextIntoCarrier(req) + } + + var statusCode int + var headers http.Header + response, err := t.rt.RoundTrip(req) + if err != nil { + statusCode = computeStatusCode(err) + } + if response != nil { + statusCode = response.StatusCode + headers = response.Header + } + + if tracer != nil { + tracer.CaptureResponse(span, headers, statusCode, trace.SpanKindClient) + } + + end := time.Now() + + // Ending the span as soon as the response is handled because we want to use the same end time for the trace and the metric. + // If any errors happen earlier, this span will be close by the defer instruction. + if span != nil { + span.End(trace.WithTimestamp(end)) + } + + if t.semConvMetricRegistry != nil && t.semConvMetricRegistry.HTTPClientRequestDuration() != nil { + var attrs []attribute.KeyValue + + if statusCode < 100 || statusCode >= 600 { + attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code %d", statusCode))) + } else if statusCode >= 400 { + attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(statusCode))) + } + + attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method)) + attrs = append(attrs, semconv.HTTPResponseStatusCode(statusCode)) + attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto))) + attrs = append(attrs, semconv.NetworkProtocolVersion(observability.Proto(req.Proto))) + attrs = append(attrs, semconv.ServerAddress(req.URL.Host)) + + _, port, err := net.SplitHostPort(req.URL.Host) + if err != nil { + switch req.URL.Scheme { + case "http": + attrs = append(attrs, semconv.ServerPort(80)) + case "https": + attrs = append(attrs, semconv.ServerPort(443)) + } + } else { + intPort, _ := strconv.Atoi(port) + attrs = append(attrs, semconv.ServerPort(intPort)) + } + + attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto"))) + + t.semConvMetricRegistry.HTTPClientRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...)) + } + + return response, err +} + +func newObservabilityRoundTripper(semConvMetricRegistry *metrics.SemConvMetricsRegistry, rt http.RoundTripper) http.RoundTripper { + return &wrapper{ + semConvMetricRegistry: semConvMetricRegistry, + rt: rt, + } +} diff --git a/pkg/server/service/observability_roundtripper_test.go b/pkg/server/service/observability_roundtripper_test.go new file mode 100644 index 000000000..afbac748e --- /dev/null +++ b/pkg/server/service/observability_roundtripper_test.go @@ -0,0 +1,123 @@ +package service + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/types" + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" +) + +func TestObservabilityRoundTripper_metrics(t *testing.T) { + tests := []struct { + desc string + serverURL string + statusCode int + wantAttributes attribute.Set + }{ + { + desc: "not found status", + serverURL: "http://www.test.com", + statusCode: http.StatusNotFound, + wantAttributes: attribute.NewSet( + attribute.Key("error.type").String("404"), + attribute.Key("http.request.method").String("GET"), + attribute.Key("http.response.status_code").Int(404), + attribute.Key("network.protocol.name").String("http/1.1"), + attribute.Key("network.protocol.version").String("1.1"), + attribute.Key("server.address").String("www.test.com"), + attribute.Key("server.port").Int(80), + attribute.Key("url.scheme").String("http"), + ), + }, + { + desc: "created status", + serverURL: "https://www.test.com", + statusCode: http.StatusCreated, + wantAttributes: attribute.NewSet( + attribute.Key("http.request.method").String("GET"), + attribute.Key("http.response.status_code").Int(201), + attribute.Key("network.protocol.name").String("http/1.1"), + attribute.Key("network.protocol.version").String("1.1"), + attribute.Key("server.address").String("www.test.com"), + attribute.Key("server.port").Int(443), + attribute.Key("url.scheme").String("http"), + ), + }, + } + + for _, test := range tests { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var cfg types.OTLP + (&cfg).SetDefaults() + cfg.AddRoutersLabels = true + cfg.PushInterval = ptypes.Duration(10 * time.Millisecond) + rdr := sdkmetric.NewManualReader() + + meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(rdr)) + // force the meter provider with manual reader to collect metrics for the test. + metrics.SetMeterProvider(meterProvider) + + semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg) + require.NoError(t, err) + require.NotNil(t, semConvMetricRegistry) + + req := httptest.NewRequest(http.MethodGet, test.serverURL+"/search?q=Opentelemetry", nil) + req.RemoteAddr = "10.0.0.1:1234" + req.Header.Set("User-Agent", "rt-test") + req.Header.Set("X-Forwarded-Proto", "http") + + ort := newObservabilityRoundTripper(semConvMetricRegistry, mockRoundTripper{statusCode: test.statusCode}) + _, err = ort.RoundTrip(req) + require.NoError(t, err) + + got := metricdata.ResourceMetrics{} + err = rdr.Collect(context.Background(), &got) + require.NoError(t, err) + + require.Len(t, got.ScopeMetrics, 1) + + expected := metricdata.Metrics{ + Name: "http.client.request.duration", + Description: "Duration of HTTP client requests.", + Unit: "s", + Data: metricdata.Histogram[float64]{ + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + Attributes: test.wantAttributes, + Count: 1, + Bounds: []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10}, + BucketCounts: []uint64{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + Min: metricdata.NewExtrema[float64](1), + Max: metricdata.NewExtrema[float64](1), + Sum: 1, + }, + }, + Temporality: metricdata.CumulativeTemporality, + }, + } + + metricdatatest.AssertEqual[metricdata.Metrics](t, expected, got.ScopeMetrics[0].Metrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue()) + }) + } +} + +type mockRoundTripper struct { + statusCode int +} + +func (m mockRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { + return &http.Response{StatusCode: m.statusCode}, nil +} diff --git a/pkg/server/service/proxy.go b/pkg/server/service/proxy.go index 083875f9e..160ee03a4 100644 --- a/pkg/server/service/proxy.go +++ b/pkg/server/service/proxy.go @@ -22,13 +22,9 @@ const StatusClientClosedRequest = 499 const StatusClientClosedRequestText = "Client Closed Request" func buildSingleHostProxy(target *url.URL, passHostHeader bool, flushInterval time.Duration, roundTripper http.RoundTripper, bufferPool httputil.BufferPool) http.Handler { - // Wrapping the roundTripper with the Tracing roundTripper, - // to handle the reverseProxy client span creation. - tracingRoundTripper := newTracingRoundTripper(roundTripper) - return &httputil.ReverseProxy{ Director: directorBuilder(target, passHostHeader), - Transport: tracingRoundTripper, + Transport: roundTripper, FlushInterval: flushInterval, BufferPool: bufferPool, ErrorHandler: errorHandler, diff --git a/pkg/server/service/proxy_test.go b/pkg/server/service/proxy_test.go index 8bea51829..a8dfd758c 100644 --- a/pkg/server/service/proxy_test.go +++ b/pkg/server/service/proxy_test.go @@ -31,7 +31,7 @@ func BenchmarkProxy(b *testing.B) { handler := buildSingleHostProxy(req.URL, false, 0, &staticTransport{res}, pool) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for range b.N { handler.ServeHTTP(w, req) } } diff --git a/pkg/server/service/roundtripper.go b/pkg/server/service/roundtripper.go index 3259e04fb..06b6473c0 100644 --- a/pkg/server/service/roundtripper.go +++ b/pkg/server/service/roundtripper.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "reflect" + "slices" "strings" "sync" "time" @@ -241,12 +242,9 @@ func (k *KerberosRoundTripper) RoundTrip(request *http.Request) (*http.Response, } func containsNTLMorNegotiate(h []string) bool { - for _, s := range h { - if strings.HasPrefix(s, "NTLM") || strings.HasPrefix(s, "Negotiate") { - return true - } - } - return false + return slices.ContainsFunc(h, func(s string) bool { + return strings.HasPrefix(s, "NTLM") || strings.HasPrefix(s, "Negotiate") + }) } func createRootCACertPool(rootCAs []types.FileOrContent) *x509.CertPool { diff --git a/pkg/server/service/roundtripper_test.go b/pkg/server/service/roundtripper_test.go index 2ca542d69..0f4a9c6e5 100644 --- a/pkg/server/service/roundtripper_test.go +++ b/pkg/server/service/roundtripper_test.go @@ -150,7 +150,7 @@ func TestKeepConnectionWhenSameConfiguration(t *testing.T) { }, } - for i := 0; i < 10; i++ { + for range 10 { rtManager.Update(dynamicConf) tr, err := rtManager.Get("test") diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index b01d2327c..f44141f84 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -22,7 +22,7 @@ import ( "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics" - tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/server/cookie" "github.com/traefik/traefik/v3/pkg/server/middleware" @@ -89,7 +89,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.H value := reflect.ValueOf(*conf.Service) var count int - for i := 0; i < value.NumField(); i++ { + for i := range value.NumField() { if !value.Field(i).IsNil() { count++ } @@ -300,30 +300,38 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName logger.Debug().Str(logs.ServerName, proxyName).Stringer("target", target). Msg("Creating server") + qualifiedSvcName := provider.GetQualifiedName(ctx, serviceName) + + if m.observabilityMgr.ShouldAddTracing(qualifiedSvcName) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName) { + // Wrapping the roundTripper with the Tracing roundTripper, + // to handle the reverseProxy client span creation. + roundTripper = newObservabilityRoundTripper(m.observabilityMgr.SemConvMetricsRegistry(), roundTripper) + } + proxy := buildSingleHostProxy(target, passHostHeader, time.Duration(flushInterval), roundTripper, m.bufferPool) // Prevents from enabling observability for internal resources. - if m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, serviceName)) { + if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName) { proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil) proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil) proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields) } if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsSvcEnabled() && - m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, serviceName)) { + m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName) { metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.observabilityMgr.MetricsRegistry(), serviceName) proxy, err = alice.New(). - Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler)). + Append(observability.WrapMiddleware(ctx, metricsHandler)). Then(proxy) if err != nil { return nil, fmt.Errorf("error wrapping metrics handler: %w", err) } } - if m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, serviceName)) { - proxy = tracingMiddle.NewService(ctx, serviceName, proxy) + if m.observabilityMgr.ShouldAddTracing(qualifiedSvcName) { + proxy = observability.NewService(ctx, serviceName, proxy) } lb.Add(proxyName, proxy, server.Weight) diff --git a/pkg/server/service/tracing_roundtripper.go b/pkg/server/service/tracing_roundtripper.go deleted file mode 100644 index bdb2ab4f3..000000000 --- a/pkg/server/service/tracing_roundtripper.go +++ /dev/null @@ -1,42 +0,0 @@ -package service - -import ( - "context" - "net/http" - - "github.com/traefik/traefik/v3/pkg/tracing" - "go.opentelemetry.io/otel/trace" -) - -type wrapper struct { - rt http.RoundTripper -} - -func (t *wrapper) RoundTrip(req *http.Request) (*http.Response, error) { - var span trace.Span - if tracer := tracing.TracerFromContext(req.Context()); tracer != nil { - var tracingCtx context.Context - tracingCtx, span = tracer.Start(req.Context(), "ReverseProxy", trace.WithSpanKind(trace.SpanKindClient)) - defer span.End() - - req = req.WithContext(tracingCtx) - - tracing.LogClientRequest(span, req) - tracing.InjectContextIntoCarrier(req) - } - - response, err := t.rt.RoundTrip(req) - if err != nil { - statusCode := computeStatusCode(err) - tracing.LogResponseCode(span, statusCode, trace.SpanKindClient) - return response, err - } - - tracing.LogResponseCode(span, response.StatusCode, trace.SpanKindClient) - - return response, nil -} - -func newTracingRoundTripper(rt http.RoundTripper) http.RoundTripper { - return &wrapper{rt: rt} -} diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go index a07f51762..5ee18caad 100644 --- a/pkg/tcp/wrr_load_balancer_test.go +++ b/pkg/tcp/wrr_load_balancer_test.go @@ -135,7 +135,7 @@ func TestLoadBalancing(t *testing.T) { } conn := &fakeConn{writeCall: make(map[string]int)} - for i := 0; i < test.totalCall; i++ { + for range test.totalCall { balancer.ServeTCP(conn) } diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index 5b1827c97..7851a8ca0 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -40,7 +40,7 @@ func (o *Options) SetDefaults() { // Store holds the options for a given Store. type Store struct { - DefaultCertificate *Certificate `json:"defaultCertificate,omitempty" toml:"defaultCertificate,omitempty" yaml:"defaultCertificate,omitempty" export:"true"` + DefaultCertificate *Certificate `json:"defaultCertificate,omitempty" toml:"defaultCertificate,omitempty" yaml:"defaultCertificate,omitempty" label:"-" export:"true"` DefaultGeneratedCert *GeneratedCert `json:"defaultGeneratedCert,omitempty" toml:"defaultGeneratedCert,omitempty" yaml:"defaultGeneratedCert,omitempty" export:"true"` } diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go index 888fc38d7..145264ca8 100644 --- a/pkg/tls/tlsmanager.go +++ b/pkg/tls/tlsmanager.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "errors" "fmt" + "slices" "strings" "sync" @@ -190,7 +191,7 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) { tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { domainToCheck := types.CanonicalDomain(clientHello.ServerName) - if isACMETLS(clientHello) { + if slices.Contains(clientHello.SupportedProtos, tlsalpn01.ACMETLS1Protocol) { certificate := acmeTLSStore.GetBestCertificate(clientHello) if certificate == nil { log.Debug().Msgf("TLS: no certificate for TLSALPN challenge: %s", domainToCheck) @@ -435,13 +436,3 @@ func buildDefaultCertificate(defaultCertificate *Certificate) (*tls.Certificate, } return &cert, nil } - -func isACMETLS(clientHello *tls.ClientHelloInfo) bool { - for _, proto := range clientHello.SupportedProtos { - if proto == tlsalpn01.ACMETLS1Protocol { - return true - } - } - - return false -} diff --git a/pkg/tracing/opentelemetry/opentelemetry_test.go b/pkg/tracing/opentelemetry/opentelemetry_test.go index 86f3a7b99..3d6ebfd16 100644 --- a/pkg/tracing/opentelemetry/opentelemetry_test.go +++ b/pkg/tracing/opentelemetry/opentelemetry_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/static" - tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing" + "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/tracing/opentelemetry" "github.com/traefik/traefik/v3/pkg/types" @@ -296,7 +296,7 @@ func TestTracing(t *testing.T) { _ = closer.Close() }) - chain := alice.New(tracingMiddle.WrapEntryPointHandler(context.Background(), newTracing, "test")) + chain := alice.New(observability.WrapEntryPointHandler(context.Background(), newTracing, nil, "test")) epHandler, err := chain.Then(service) require.NoError(t, err) diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index a3e1623c7..8faef513d 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "strconv" + "strings" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/static" @@ -26,7 +27,7 @@ type Backend interface { } // NewTracing Creates a Tracing. -func NewTracing(conf *static.Tracing) (trace.Tracer, io.Closer, error) { +func NewTracing(conf *static.Tracing) (*Tracer, io.Closer, error) { var backend Backend if conf.OTLP != nil { @@ -41,11 +42,16 @@ func NewTracing(conf *static.Tracing) (trace.Tracer, io.Closer, error) { otel.SetTextMapPropagator(autoprop.NewTextMapPropagator()) - return backend.Setup(conf.ServiceName, conf.SampleRate, conf.GlobalAttributes) + tr, closer, err := backend.Setup(conf.ServiceName, conf.SampleRate, conf.GlobalAttributes) + if err != nil { + return nil, nil, err + } + + return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders), closer, nil } // TracerFromContext extracts the trace.Tracer from the given context. -func TracerFromContext(ctx context.Context) trace.Tracer { +func TracerFromContext(ctx context.Context) *Tracer { // Prevent picking trace.noopSpan tracer. if !trace.SpanContextFromContext(ctx).IsValid() { return nil @@ -53,7 +59,12 @@ func TracerFromContext(ctx context.Context) trace.Tracer { span := trace.SpanFromContext(ctx) if span != nil && span.TracerProvider() != nil { - return span.TracerProvider().Tracer("github.com/traefik/traefik") + tracer := span.TracerProvider().Tracer("github.com/traefik/traefik") + if tracer, ok := tracer.(*Tracer); ok { + return tracer + } + + return nil } return nil @@ -78,10 +89,73 @@ func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) { } } -// LogClientRequest used to add span attributes from the request as a Client. -// TODO: the semconv does not implement Semantic Convention v1.23.0. -func LogClientRequest(span trace.Span, r *http.Request) { - if r == nil || span == nil { +// Span is trace.Span wrapping the Traefik TracerProvider. +type Span struct { + trace.Span + + tracerProvider *TracerProvider +} + +// TracerProvider returns the span's TraceProvider. +func (s Span) TracerProvider() trace.TracerProvider { + return s.tracerProvider +} + +// TracerProvider is trace.TracerProvider wrapping the Traefik Tracer implementation. +type TracerProvider struct { + trace.TracerProvider + + tracer *Tracer +} + +// Tracer returns the trace.Tracer for the given options. +// It returns specifically the Traefik Tracer when requested. +func (t TracerProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer { + if name == "github.com/traefik/traefik" { + return t.tracer + } + + return t.TracerProvider.Tracer(name, options...) +} + +// Tracer is trace.Tracer with additional properties. +type Tracer struct { + trace.Tracer + + capturedRequestHeaders []string + capturedResponseHeaders []string +} + +// NewTracer builds and configures a new Tracer. +func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders []string) *Tracer { + return &Tracer{ + Tracer: tracer, + capturedRequestHeaders: capturedRequestHeaders, + capturedResponseHeaders: capturedResponseHeaders, + } +} + +// Start starts a new span. +// spancheck linter complains about span.End not being called, but this is expected here, +// hence its deactivation. +// +//nolint:spancheck +func (t *Tracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + if t == nil { + return ctx, nil + } + + spanCtx, span := t.Tracer.Start(ctx, spanName, opts...) + + wrappedSpan := &Span{Span: span, tracerProvider: &TracerProvider{tracer: t}} + + return trace.ContextWithSpan(spanCtx, wrappedSpan), wrappedSpan +} + +// CaptureClientRequest used to add span attributes from the request as a Client. +// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0. +func (t *Tracer) CaptureClientRequest(span trace.Span, r *http.Request) { + if t == nil || span == nil || r == nil { return } @@ -113,12 +187,23 @@ func LogClientRequest(span trace.Span, r *http.Request) { span.SetAttributes(semconv.ServerAddress(host)) span.SetAttributes(semconv.ServerPort(intPort)) } + + for _, header := range t.capturedRequestHeaders { + // User-agent is already part of the semantic convention as a recommended attribute. + if strings.EqualFold(header, "User-Agent") { + continue + } + + if value := r.Header[header]; value != nil { + span.SetAttributes(attribute.StringSlice(fmt.Sprintf("http.request.header.%s", strings.ToLower(header)), value)) + } + } } -// LogServerRequest used to add span attributes from the request as a Server. -// TODO: the semconv does not implement Semantic Convention v1.23.0. -func LogServerRequest(span trace.Span, r *http.Request) { - if r == nil { +// CaptureServerRequest used to add span attributes from the request as a Server. +// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0. +func (t *Tracer) CaptureServerRequest(span trace.Span, r *http.Request) { + if t == nil || span == nil || r == nil { return } @@ -147,6 +232,45 @@ func LogServerRequest(span trace.Span, r *http.Request) { } span.SetAttributes(semconv.ClientSocketAddress(r.Header.Get("X-Forwarded-For"))) + + for _, header := range t.capturedRequestHeaders { + // User-agent is already part of the semantic convention as a recommended attribute. + if strings.EqualFold(header, "User-Agent") { + continue + } + + if value := r.Header[header]; value != nil { + span.SetAttributes(attribute.StringSlice(fmt.Sprintf("http.request.header.%s", strings.ToLower(header)), value)) + } + } +} + +// CaptureResponse captures the response attributes to the span. +func (t *Tracer) CaptureResponse(span trace.Span, responseHeaders http.Header, code int, spanKind trace.SpanKind) { + if t == nil || span == nil { + return + } + + var status codes.Code + var desc string + switch spanKind { + case trace.SpanKindServer: + status, desc = serverStatus(code) + case trace.SpanKindClient: + status, desc = clientStatus(code) + default: + status, desc = defaultStatus(code) + } + span.SetStatus(status, desc) + if code > 0 { + span.SetAttributes(semconv.HTTPResponseStatusCode(code)) + } + + for _, header := range t.capturedResponseHeaders { + if value := responseHeaders[header]; value != nil { + span.SetAttributes(attribute.StringSlice(fmt.Sprintf("http.response.header.%s", strings.ToLower(header)), value)) + } + } } func proto(proto string) string { @@ -164,30 +288,10 @@ func proto(proto string) string { } } -// LogResponseCode used to log response code in span. -func LogResponseCode(span trace.Span, code int, spanKind trace.SpanKind) { - if span != nil { - var status codes.Code - var desc string - switch spanKind { - case trace.SpanKindServer: - status, desc = ServerStatus(code) - case trace.SpanKindClient: - status, desc = ClientStatus(code) - default: - status, desc = DefaultStatus(code) - } - span.SetStatus(status, desc) - if code > 0 { - span.SetAttributes(semconv.HTTPResponseStatusCode(code)) - } - } -} - -// ServerStatus returns a span status code and message for an HTTP status code +// serverStatus returns a span status code and message for an HTTP status code // value returned by a server. Status codes in the 400-499 range are not // returned as errors. -func ServerStatus(code int) (codes.Code, string) { +func serverStatus(code int) (codes.Code, string) { if code < 100 || code >= 600 { return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) } @@ -197,10 +301,10 @@ func ServerStatus(code int) (codes.Code, string) { return codes.Unset, "" } -// ClientStatus returns a span status code and message for an HTTP status code +// clientStatus returns a span status code and message for an HTTP status code // value returned by a server. Status codes in the 400-499 range are not // returned as errors. -func ClientStatus(code int) (codes.Code, string) { +func clientStatus(code int) (codes.Code, string) { if code < 100 || code >= 600 { return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) } @@ -210,9 +314,9 @@ func ClientStatus(code int) (codes.Code, string) { return codes.Unset, "" } -// DefaultStatus returns a span status code and message for an HTTP status code +// defaultStatus returns a span status code and message for an HTTP status code // value generated internally. -func DefaultStatus(code int) (codes.Code, string) { +func defaultStatus(code int) (codes.Code, string) { if code < 100 || code >= 600 { return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) } diff --git a/pkg/types/http_code_range.go b/pkg/types/http_code_range.go index 4a8f541a5..b00ac1957 100644 --- a/pkg/types/http_code_range.go +++ b/pkg/types/http_code_range.go @@ -1,6 +1,7 @@ package types import ( + "slices" "strconv" "strings" ) @@ -34,10 +35,7 @@ func NewHTTPCodeRanges(strBlocks []string) (HTTPCodeRanges, error) { // Contains tests whether the passed status code is within one of its HTTP code ranges. func (h HTTPCodeRanges) Contains(statusCode int) bool { - for _, block := range h { - if statusCode >= block[0] && statusCode <= block[1] { - return true - } - } - return false + return slices.ContainsFunc(h, func(block [2]int) bool { + return statusCode >= block[0] && statusCode <= block[1] + }) } diff --git a/pkg/types/metrics.go b/pkg/types/metrics.go index 28b151a08..cde08e189 100644 --- a/pkg/types/metrics.go +++ b/pkg/types/metrics.go @@ -125,7 +125,7 @@ func (o *OTLP) SetDefaults() { o.AddEntryPointsLabels = true o.AddServicesLabels = true - o.ExplicitBoundaries = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} + o.ExplicitBoundaries = []float64{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10} o.PushInterval = types.Duration(10 * time.Second) } diff --git a/pkg/udp/conn_test.go b/pkg/udp/conn_test.go index 3c8cc0a7f..fbcd080b3 100644 --- a/pkg/udp/conn_test.go +++ b/pkg/udp/conn_test.go @@ -197,7 +197,7 @@ func testTimeout(t *testing.T, withRead bool) { } }() - for i := 0; i < 10; i++ { + for range 10 { udpConn2, err := net.Dial("udp", ln.Addr().String()) require.NoError(t, err) diff --git a/script/gcg/traefik-rc-new.toml b/script/gcg/traefik-rc-new.toml index 8f2f335f1..e13057267 100644 --- a/script/gcg/traefik-rc-new.toml +++ b/script/gcg/traefik-rc-new.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example rc1 of v3.0.0 +# example rc3 of v3.0.0 CurrentRef = "v3.0" -PreviousRef = "v3.0.0-beta5" +PreviousRef = "v3.0.0-rc2" BaseBranch = "v3.0" -FutureCurrentRefName = "v3.0.0-rc1" +FutureCurrentRefName = "v3.0.0-rc3" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/webui/.eslintignore b/webui/.eslintignore index 9b1c8b133..9f81cf845 100644 --- a/webui/.eslintignore +++ b/webui/.eslintignore @@ -1 +1,7 @@ /dist +/src-capacitor +/src-cordova +/.quasar +/node_modules +.eslintrc.cjs +/quasar.config.*.temporary.compiled* diff --git a/webui/.eslintrc.js b/webui/.eslintrc.cjs similarity index 62% rename from webui/.eslintrc.js rename to webui/.eslintrc.cjs index f8fee91f8..c08b11557 100644 --- a/webui/.eslintrc.js +++ b/webui/.eslintrc.cjs @@ -2,21 +2,24 @@ module.exports = { root: true, parserOptions: { - parser: 'babel-eslint', - sourceType: 'module' + parser: '@babel/eslint-parser', + ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features }, env: { + node: true, browser: true, - mocha: true + mocha: true, + 'vue/setup-compiler-macros': true }, extends: [ // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. - 'plugin:vue/essential', - '@vue/standard', - 'plugin:mocha/recommended' + 'plugin:vue/vue3-essential', + 'plugin:vue/vue3-recommended', + 'plugin:mocha/recommended', + 'standard' ], // required to lint *.vue files @@ -26,10 +29,16 @@ module.exports = { ], globals: { - 'ga': true, // Google Analytics - 'cordova': true, - '__statics': true, - 'process': true + ga: 'readonly', // Google Analytics + cordova: 'readonly', + __statics: 'readonly', + __QUASAR_SSR__: 'readonly', + __QUASAR_SSR_SERVER__: 'readonly', + __QUASAR_SSR_CLIENT__: 'readonly', + __QUASAR_SSR_PWA__: 'readonly', + process: 'readonly', + Capacitor: 'readonly', + chrome: 'readonly' }, // add your custom rules here @@ -39,6 +48,8 @@ module.exports = { // allow paren-less arrow functions 'arrow-parens': 'off', 'one-var': 'off', + 'no-void': 'off', + 'multiline-ternary': 'off', 'import/first': 'off', 'import/named': 'error', @@ -49,6 +60,7 @@ module.exports = { 'import/no-unresolved': 'off', 'import/no-extraneous-dependencies': 'off', 'prefer-promise-reject-errors': 'off', + 'vue/multi-word-component-names': 'off', // allow console.log during development only //'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', @@ -56,3 +68,4 @@ module.exports = { //'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' } } + diff --git a/webui/.postcssrc.js b/webui/.postcssrc.cjs similarity index 100% rename from webui/.postcssrc.js rename to webui/.postcssrc.cjs diff --git a/webui/babel.config.cjs b/webui/babel.config.cjs new file mode 100644 index 000000000..cd63d3933 --- /dev/null +++ b/webui/babel.config.cjs @@ -0,0 +1,16 @@ +/* eslint-disable */ + +module.exports = api => { + return { + presets: [ + [ + '@quasar/babel-preset-app', + api.caller(caller => caller && caller.target === 'node') + ? { targets: { node: 'current' } } + : {} + ] + ] + } +} + + diff --git a/webui/babel.config.js b/webui/babel.config.js deleted file mode 100644 index 9408c6cd4..000000000 --- a/webui/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@quasar/babel-preset-app' - ] -} diff --git a/webui/src/index.template.html b/webui/index.html similarity index 94% rename from webui/src/index.template.html rename to webui/index.html index 70fee7d64..6eb6d6091 100644 --- a/webui/src/index.template.html +++ b/webui/index.html @@ -20,7 +20,6 @@ - -
+ diff --git a/webui/jsconfig.json b/webui/jsconfig.json new file mode 100644 index 000000000..456944a5e --- /dev/null +++ b/webui/jsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": [ + "src/*" + ], + "app/*": [ + "*" + ], + "components/*": [ + "src/components/*" + ], + "layouts/*": [ + "src/layouts/*" + ], + "pages/*": [ + "src/pages/*" + ], + "assets/*": [ + "src/assets/*" + ], + "boot/*": [ + "src/boot/*" + ], + "stores/*": [ + "src/stores/*" + ], + "vue$": [ + "node_modules/vue/dist/vue.runtime.esm-bundler.js" + ] + } + }, + "exclude": [ + "dist", + ".quasar", + "node_modules" + ] +} \ No newline at end of file diff --git a/webui/package.json b/webui/package.json index f114f2ce8..77ebdd049 100644 --- a/webui/package.json +++ b/webui/package.json @@ -16,42 +16,45 @@ "build:nc": "yarn build" }, "dependencies": { - "@quasar/extras": "^1.11.2", - "axios": "^0.21.1", - "bowser": "^2.5.2", - "chart.js": "^2.8.0", - "dot-prop": "^5.2.0", + "@quasar/extras": "^1.16.9", + "axios": "^1.6.7", + "bowser": "^2.11.0", + "chart.js": "^4.4.1", "core-js": "^3.35.1", - "iframe-resizer": "^4.2.11", + "dot-prop": "^8.0.2", + "iframe-resizer": "^4.3.9", "lodash.isequal": "4.5.0", - "moment": "^2.24.0", - "quasar": "^1.22.10", - "query-string": "^6.13.1", + "moment": "^2.30.1", + "quasar": "^2.14.3", + "query-string": "^8.1.0", "vh-check": "^2.0.5", - "vue-chartjs": "^3.4.2", - "vuex-map-fields": "^1.3.4" + "vue": "^3.0.0", + "vue-chartjs": "^5.3.0", + "vue-router": "^4.0.12", + "vuex": "^4.1.0", + "vuex-map-fields": "^1.4.1" }, "devDependencies": { - "@quasar/app": "^2.4.3", - "@vue/eslint-config-standard": "^4.0.0", - "@vue/test-utils": "^1.0.0-beta.29", - "babel-eslint": "^10.0.1", - "chai": "4.2.0", - "eslint": "^5.10.0", - "eslint-loader": "^2.1.1", - "eslint-plugin-prettier": "3.1.1", - "eslint-plugin-mocha": "6.2.1", - "eslint-plugin-vue": "^5.0.0", - "mocha": "^6.2.2", - "mocha-webpack": "^2.0.0-beta.0", - "prettier": "1.19.1" + "@babel/core": "^7.23.9", + "@babel/eslint-parser": "^7.23.10", + "@quasar/app-vite": "^1.4.3", + "@quasar/babel-preset-app": "^2.0.2", + "@vue/test-utils": "^2.4.4", + "autoprefixer": "^10.4.2", + "chai": "5.0.3", + "eslint": "^8.11.0", + "eslint-config-standard": "^17.0.0", + "eslint-plugin-import": "^2.19.1", + "eslint-plugin-mocha": "^10.2.0", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-vue": "^9.0.0", + "mocha": "^10.2.0", + "postcss": "^8.4.14" }, "engines": { - "node": ">= 8.9.0", - "npm": ">= 5.6.0", - "yarn": ">= 1.6.0" - }, - "browserslist": [ - "last 1 version, not dead, ie >= 11" - ] + "node": "^20 || ^18 || ^16", + "npm": ">= 6.13.4", + "yarn": ">= 1.21.1" + } } diff --git a/webui/postcss.config.cjs b/webui/postcss.config.cjs new file mode 100644 index 000000000..94b7b1c85 --- /dev/null +++ b/webui/postcss.config.cjs @@ -0,0 +1,27 @@ +/* eslint-disable */ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // https://github.com/postcss/autoprefixer + require('autoprefixer')({ + overrideBrowserslist: [ + 'last 4 Chrome versions', + 'last 4 Firefox versions', + 'last 4 Edge versions', + 'last 4 Safari versions', + 'last 4 Android versions', + 'last 4 ChromeAndroid versions', + 'last 4 FirefoxAndroid versions', + 'last 4 iOS versions' + ] + }) + + // https://github.com/elchininet/postcss-rtlcss + // If you want to support RTL css, then + // 1. yarn/npm install postcss-rtlcss + // 2. optionally set quasar.config.js > framework > lang to an RTL language + // 3. uncomment the following line: + // require('postcss-rtlcss') + ] +} diff --git a/webui/quasar.conf.js b/webui/quasar.conf.js index 1e7bf08a5..930136d3a 100644 --- a/webui/quasar.conf.js +++ b/webui/quasar.conf.js @@ -1,12 +1,18 @@ // Configuration for your app // https://quasar.dev/quasar-cli/quasar-conf-js -module.exports = function (ctx) { +const { configure } = require('quasar/wrappers') + +module.exports = configure(function (ctx) { return { + eslint: { + warnings: true, + errors: true + }, + // app boot file (/src/boot) // --> boot files are part of "main.js" boot: [ - '_globals', 'api', '_hacks', '_init' @@ -114,6 +120,22 @@ module.exports = function (ctx) { supportIE: false, build: { + // Needed to have relative assets in the index.html + // https://github.com/quasarframework/quasar/issues/8513#issuecomment-1127654470 + extendViteConf(viteConf, {isServer, isClient}) { + viteConf.base = ""; + }, + viteVuePluginOptions: { + template: { + compilerOptions: { + isCustomElement: (tag) => tag.startsWith('hub-') + } + } + }, + target: { + browser: ['edge88', 'firefox78', 'chrome87', 'safari13.1'], + node: 'node20' + }, publicPath: process.env.APP_PUBLIC_PATH || '', env: process.env.APP_ENV === 'development' ? { // staging: @@ -131,22 +153,7 @@ module.exports = function (ctx) { } }, scopeHoisting: true, - // vueRouterMode: 'history', - // vueCompiler: true, - // gzip: true, - // analyze: true, - // extractCSS: false, - extendWebpack (cfg) { - cfg.module.rules.push({ - enforce: 'pre', - test: /\.(js|vue)$/, - loader: 'eslint-loader', - exclude: /node_modules/, - options: { - formatter: require('eslint').CLIEngine.getFormatter('stylish') - } - }) - } + vueRouterMode: 'hash' // available values: 'hash', 'history' }, devServer: { @@ -166,16 +173,24 @@ module.exports = function (ctx) { animations: [], ssr: { - pwa: false + pwa: false, }, pwa: { + + workboxMode: 'injectManifest', // or 'generateSW' // workboxPluginMode: 'InjectManifest', // workboxOptions: {}, // only for NON InjectManifest workboxOptions: { skipWaiting: true, clientsClaim: true }, + + chainWebpackCustomSW (chain) { + chain.plugin('eslint-webpack-plugin') + .use(ESLintPlugin, [{ extensions: ['js'] }]) + }, + manifest: { // name: 'Traefik', // short_name: 'Traefik', @@ -247,4 +262,4 @@ module.exports = function (ctx) { } } } -} +}) diff --git a/webui/src/App.vue b/webui/src/App.vue index d655741b1..824ed04e6 100644 --- a/webui/src/App.vue +++ b/webui/src/App.vue @@ -13,6 +13,11 @@ export default { computed: { ...mapGetters('core', { coreVersion: 'version' }) }, + watch: { + '$q.dark.isActive' (val) { + localStorage.setItem('traefik-dark', val) + } + }, beforeCreate () { // Set vue instance APP.vue = () => this.$root @@ -21,11 +26,6 @@ export default { console.log('Quasar -> ', this.$q.version) this.$q.dark.set(localStorage.getItem('traefik-dark') === 'true') - }, - watch: { - '$q.dark.isActive' (val) { - localStorage.setItem('traefik-dark', val) - } } } diff --git a/webui/src/_directives/resize.js b/webui/src/_directives/resize.js index 59411eb2f..3a0500a44 100644 --- a/webui/src/_directives/resize.js +++ b/webui/src/_directives/resize.js @@ -1,8 +1,16 @@ -import Vue from 'vue' -import iFrameResize from 'iframe-resizer/js/iframeResizer' +import iframeResize from 'iframe-resizer/js/iframeResizer' -Vue.directive('resize', { - bind: function (el, { value = {} }) { - el.addEventListener('load', () => iFrameResize(value, el)) +const resize = { + mounted (el, binding) { + const options = binding.value || {} + el.addEventListener('load', () => iframeResize(options, el)) + }, + unmounted (el) { + const resizableEl = el + if (resizableEl.iFrameResizer) { + resizableEl.iFrameResizer.removeListeners() + } } -}) +} + +export default resize diff --git a/webui/src/_helpers/Errors.js b/webui/src/_helpers/Errors.js index 749885076..482766f16 100644 --- a/webui/src/_helpers/Errors.js +++ b/webui/src/_helpers/Errors.js @@ -22,7 +22,7 @@ class Errors { static handleResponse (error) { console.log('handleResponse', error, error.response) - let body = error.response.data + const body = error.response.data if (error.response.status === 401) { // TODO - actions... } diff --git a/webui/src/_helpers/Helps.js b/webui/src/_helpers/Helps.js index b8ba03d9e..96aef3e62 100644 --- a/webui/src/_helpers/Helps.js +++ b/webui/src/_helpers/Helps.js @@ -1,4 +1,4 @@ -import { get } from 'dot-prop' +import { getProperty } from 'dot-prop' class Helps { // Getters @@ -11,7 +11,7 @@ class Helps { // ------------------------------------------------------------------------ static get (obj, prop, def = undefined) { - return get(obj, prop, def) + return getProperty(obj, prop, def) } static hasIn (obj, prop) { @@ -39,13 +39,12 @@ class Helps { } static removeEmptyObjects (objects) { - const obj = {} - Object.entries(objects).map(item => { - if (item[1] !== '') { - obj[item[0]] = item[1] - } - }) - return obj + Object.entries(objects) + .filter(item => item[1] !== '') + .reduce((acc, item) => { + acc[item[0]] = item[1] + return acc + }, {}) } // Helps -> Numbers diff --git a/webui/src/_helpers/Mutations.js b/webui/src/_helpers/Mutations.js index 71faeec30..6550fb696 100644 --- a/webui/src/_helpers/Mutations.js +++ b/webui/src/_helpers/Mutations.js @@ -1,8 +1,8 @@ -import { set, get } from 'dot-prop' +import { setProperty, getProperty } from 'dot-prop' export const withPagination = (type, opts = {}) => (state, data) => { const { isSameContext, statePath } = opts - const currentState = get(state, statePath) + const currentState = getProperty(state, statePath) let newState @@ -13,7 +13,7 @@ export const withPagination = (type, opts = {}) => (state, data) => { loading: true } break - case 'success': + case 'success': { const { body, page } = data newState = { ...currentState, @@ -28,6 +28,7 @@ export const withPagination = (type, opts = {}) => (state, data) => { loading: false } break + } case 'failure': newState = { ...currentState, @@ -39,6 +40,6 @@ export const withPagination = (type, opts = {}) => (state, data) => { } if (newState) { - set(state, statePath, newState) + setProperty(state, statePath, newState) } } diff --git a/webui/src/_middleware/Boot.js b/webui/src/_middleware/Boot.js index 8fc5c968b..378794516 100644 --- a/webui/src/_middleware/Boot.js +++ b/webui/src/_middleware/Boot.js @@ -4,6 +4,11 @@ import Helps from '../_helpers/Helps' const Boot = { install (Vue, options) { Vue.mixin({ + filters: { + capFirstLetter (value) { + return Helps.capFirstLetter(value) + } + }, data () { return { } @@ -28,14 +33,9 @@ const Boot = { } } }, - methods: { - }, - filters: { - capFirstLetter (value) { - return Helps.capFirstLetter(value) - } - }, created () { + }, + methods: { } }) } diff --git a/webui/src/_mixins/GetTableProps.js b/webui/src/_mixins/GetTableProps.js index b58d8be23..9a64e2586 100644 --- a/webui/src/_mixins/GetTableProps.js +++ b/webui/src/_mixins/GetTableProps.js @@ -1,9 +1,9 @@ -import { get } from 'dot-prop' +import { getProperty } from 'dot-prop' import { QChip } from 'quasar' -import Chips from '../components/_commons/Chips' -import ProviderIcon from '../components/_commons/ProviderIcon' -import AvatarState from '../components/_commons/AvatarState' -import TLSState from '../components/_commons/TLSState' +import Chips from '../components/_commons/Chips.vue' +import ProviderIcon from '../components/_commons/ProviderIcon.vue' +import AvatarState from '../components/_commons/AvatarState.vue' +import TLSState from '../components/_commons/TLSState.vue' const allColumns = [ { @@ -165,7 +165,7 @@ const GetTablePropsMixin = { path: `/${type.replace('-', '/', 'gi')}/${encodeURIComponent(row.name)}` }), columns: allColumns.filter(c => - get(propsByType, `${type}.columns`, []).includes(c.name) + getProperty(propsByType, `${type}.columns`, []).includes(c.name) ) } } diff --git a/webui/src/_mixins/Pagination.js b/webui/src/_mixins/Pagination.js index 4c2091626..a090beac2 100644 --- a/webui/src/_mixins/Pagination.js +++ b/webui/src/_mixins/Pagination.js @@ -1,4 +1,4 @@ -import { get } from 'dot-prop' +import { getProperty } from 'dot-prop' export default function PaginationMixin (opts = {}) { const { pollingIntervalTime, rowsPerPage = 10 } = opts @@ -28,7 +28,7 @@ export default function PaginationMixin (opts = {}) { currentPage = page currentLimit = limit || rowsPerPage - const fetchMethod = get(this, opts.fetchMethod) + const fetchMethod = getProperty(this, opts.fetchMethod) return fetchMethod({ ...params, @@ -41,7 +41,7 @@ export default function PaginationMixin (opts = {}) { }) }, initFetch (params) { - const scrollerRef = get(this.$refs, opts.scrollerRef) + const scrollerRef = getProperty(this.$refs, opts.scrollerRef) if (scrollerRef) { scrollerRef.stop() diff --git a/webui/src/boot/_globals.js b/webui/src/boot/_globals.js index f23935ca9..f099d03cd 100644 --- a/webui/src/boot/_globals.js +++ b/webui/src/boot/_globals.js @@ -1,8 +1,8 @@ import { APP } from '../_helpers/APP' import Boot from '../_middleware/Boot' -export default async ({ app, router, store, Vue }) => { - Vue.use(Boot) +export default async ({ app, router, store }) => { + app.use(Boot) APP.root = app APP.router = router diff --git a/webui/src/boot/_init.js b/webui/src/boot/_init.js index 9c78853ae..5ec3caf4e 100644 --- a/webui/src/boot/_init.js +++ b/webui/src/boot/_init.js @@ -1,10 +1,14 @@ import { APP } from '../_helpers/APP' import errors from '../_helpers/Errors' +import resize from '../_directives/resize' + +export default async ({ app, router }) => { + // Directives + app.directive('resize', resize) -export default async ({ Vue }) => { // Router // ---------------------------------------------- - APP.router.beforeEach(async (to, from, next) => { + router.beforeEach(async (to, from, next) => { // Set APP APP.routeTo = to APP.routeFrom = from diff --git a/webui/src/boot/api.js b/webui/src/boot/api.js index 706330d8c..8d4f5b314 100644 --- a/webui/src/boot/api.js +++ b/webui/src/boot/api.js @@ -1,12 +1,16 @@ +import { boot } from 'quasar/wrappers' import axios from 'axios' - import { APP } from '../_helpers/APP' // Set config defaults when creating the instance -const API = axios.create({ +const api = axios.create({ baseURL: APP.config.apiUrl }) -export default async ({ app, Vue }) => { - Vue.prototype.$api = app.api = APP.api = API -} +export default boot(({ app }) => { + app.config.globalProperties.$axios = axios + app.config.globalProperties.$api = api + APP.api = api +}) + +export { api } diff --git a/webui/src/components/_commons/AvatarState.vue b/webui/src/components/_commons/AvatarState.vue index a0adcd002..e74e73104 100644 --- a/webui/src/components/_commons/AvatarState.vue +++ b/webui/src/components/_commons/AvatarState.vue @@ -1,16 +1,32 @@