diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index 1765a242b..15634aecd 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -99,38 +99,6 @@ helm install traefik traefik/traefik - "--log.level=DEBUG" ``` -### Exposing the Traefik dashboard - -This Helm chart does not expose the Traefik dashboard by default, for security concerns. -Thus, there are multiple ways to expose the dashboard. -For instance, the dashboard access could be achieved through a port-forward: - -```shell -kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000 -``` - -It can then be reached at: `http://127.0.0.1:9000/dashboard/` - -Another way would be to apply your own configuration, for instance, -by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`): - -```yaml -# dashboard.yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: dashboard -spec: - entryPoints: - - web - routes: - - match: Host(`traefik.localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)) - kind: Rule - services: - - name: api@internal - kind: TraefikService -``` - ## Use the Binary Distribution Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page. diff --git a/docs/content/getting-started/quick-start-with-kubernetes.md b/docs/content/getting-started/quick-start-with-kubernetes.md index cfbbb7259..c371f0072 100644 --- a/docs/content/getting-started/quick-start-with-kubernetes.md +++ b/docs/content/getting-started/quick-start-with-kubernetes.md @@ -36,6 +36,7 @@ rules: resources: - services - secrets + - nodes verbs: - get - list @@ -64,6 +65,23 @@ rules: - ingresses/status verbs: - update + - apiGroups: + - traefik.io + resources: + - middlewares + - middlewaretcps + - ingressroutes + - traefikservices + - ingressroutetcps + - ingressrouteudps + - tlsoptions + - tlsstores + - serverstransports + - serverstransporttcps + verbs: + - get + - list + - watch ``` !!! info "You can find the reference for this file [there](../../reference/dynamic-configuration/kubernetes-crd/#rbac)." diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 44a53641f..b73e6cc2f 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -341,6 +341,7 @@ For complete details, refer to your provider's _Additional configuration_ link. | [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) | +| [DirectAdmin](https://www.directadmin.com) | `directadmin` | `DIRECTADMIN_API_URL` , `DIRECTADMIN_USERNAME`, `DIRECTADMIN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/directadmin) | | [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) | | [dnsHome.de](https://www.dnshome.de) | `dnsHomede` | `DNSHOMEDE_CREDENTIALS` | [Additional configuration](https://go-acme.github.io/lego/dns/dnshomede) | | [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) | @@ -384,12 +385,15 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) | | [Liara](https://liara.ir) | `liara` | `LIARA_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/liara) | | [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) | +| [Lima-City](https://www.lima-city.de) | `limacity` | `LIMACITY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/limacity) | | [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) | | [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) | | [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) | +| [mijn.host](https://mijn.host/) | `mijnhost` | `MIJNHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/mijnhost) | +| [Mittwald](https://www.mittwald.de) | `mittwald` | `MITTWALD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/mittwald) | | [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) | | [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) | @@ -418,8 +422,8 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) | | [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) | | [Scaleway](https://www.scaleway.com) | `scaleway` | `SCW_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) | | [Selectel v2](https://selectel.ru/en/) | `selectelv2` | `SELECTELV2_ACCOUNT_ID`, `SELECTELV2_PASSWORD`, `SELECTELV2_PROJECT_ID`, `SELECTELV2_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/selectelv2) | +| [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) | diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index f5481eb96..eb30e0200 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -250,6 +250,8 @@ accessLog: | `TLSVersion` | The TLS version used by the connection (e.g. `1.2`) (if connection is TLS). | | `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS) | | `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`) | + | `TraceId` | A consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string | + | `SpanId` | A unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. | ## Log Rotation diff --git a/docs/content/observability/tracing/overview.md b/docs/content/observability/tracing/overview.md index fcea5c548..b69aee46b 100644 --- a/docs/content/observability/tracing/overview.md +++ b/docs/content/observability/tracing/overview.md @@ -170,7 +170,6 @@ Defines the list of query parameters to not redact. ```yaml tab="File (YAML)" tracing: - otlp: safeQueryParams: - bar - buz @@ -178,10 +177,9 @@ tracing: ```toml tab="File (TOML)" [tracing] - [tracing.otlp] safeQueryParams = ["bar", "buz"] ``` ```bash tab="CLI" ---tracing.otlp.safeQueryParams=bar,buz +--tracing.safeQueryParams=bar,buz ``` diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 62958ccd4..269467380 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -82,6 +82,7 @@ [http.services.Service03] [http.services.Service03.mirroring] service = "foobar" + mirrorBody = true maxBodySize = 42 [[http.services.Service03.mirroring.mirrors]] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index e4e82bea4..6f94af243 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -89,6 +89,7 @@ http: Service03: mirroring: service: foobar + mirrorBody: true maxBodySize: 42 mirrors: - name: foobar diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index 30038945a..96816d8a4 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -2506,6 +2506,11 @@ spec: Default value is -1, which means unlimited size. format: int64 type: integer + mirrorBody: + description: |- + MirrorBody defines whether the body of the request should be mirrored. + Default value is true. + type: boolean mirrors: description: Mirrors defines the list of mirrors where Traefik will duplicate the traffic. diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-resource.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-resource.yml index 93f1a95ff..14e809a2e 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-resource.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-resource.yml @@ -63,6 +63,7 @@ spec: mirroring: name: wrr2 kind: TraefikService + mirrorBody: true # Optional maxBodySize: 2000000000 mirrors: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 205ef0efe..bd9b99a7a 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -264,6 +264,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | | `traefik/http/services/Service03/mirroring/healthCheck` | `` | | `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | +| `traefik/http/services/Service03/mirroring/mirrorBody` | `true` | | `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` | | `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` | | `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index 1e1b279d5..48e629bb8 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -121,6 +121,11 @@ spec: Default value is -1, which means unlimited size. format: int64 type: integer + mirrorBody: + description: |- + MirrorBody defines whether the body of the request should be mirrored. + Default value is true. + type: boolean mirrors: description: Mirrors defines the list of mirrors where Traefik will duplicate the traffic. diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index dad91d472..8e4cc41f0 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -1207,6 +1207,7 @@ http: The mirroring is able to mirror requests sent to a service to other services. Please note that by default the whole request is buffered in memory while it is being mirrored. See the maxBodySize option in the example below for how to modify this behaviour. +You can also omit the request body by setting the mirrorBody option to `false`. !!! info "Supported Providers" @@ -1219,6 +1220,9 @@ http: mirrored-api: mirroring: service: appv1 + # mirrorBody defines whether the request body should be mirrored. + # Default value is true. + mirrorBody: false # maxBodySize is the maximum size allowed for the body of the request. # If the body is larger, the request is not mirrored. # Default value is -1, which means unlimited size. @@ -1248,6 +1252,9 @@ http: # If the body is larger, the request is not mirrored. # Default value is -1, which means unlimited size. maxBodySize = 1024 + # mirrorBody defines whether the request body should be mirrored. + # Default value is true. + mirrorBody = false [[http.services.mirrored-api.mirroring.mirrors]] name = "appv2" percent = 10 diff --git a/go.mod b/go.mod index 5ff469aec..545e61b52 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/fatih/structs v1.1.0 github.com/fsnotify/fsnotify v1.7.0 - github.com/go-acme/lego/v4 v4.17.4 + github.com/go-acme/lego/v4 v4.18.0 github.com/go-kit/kit v0.13.0 github.com/go-kit/log v0.2.1 github.com/golang/protobuf v1.5.4 @@ -64,7 +64,7 @@ require ( github.com/tetratelabs/wazero v1.7.2 github.com/tidwall/gjson v1.17.0 github.com/traefik/grpc-web v0.16.0 - github.com/traefik/paerser v0.2.0 + github.com/traefik/paerser v0.2.1 github.com/traefik/yaegi v0.16.1 github.com/unrolled/render v1.0.2 github.com/unrolled/secure v1.0.9 @@ -85,8 +85,8 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // No tag on the repo. golang.org/x/mod v0.18.0 golang.org/x/net v0.26.0 - golang.org/x/sys v0.21.0 - golang.org/x/text v0.16.0 + golang.org/x/sys v0.23.0 + golang.org/x/text v0.17.0 golang.org/x/time v0.5.0 golang.org/x/tools v0.22.0 google.golang.org/grpc v1.64.1 @@ -221,7 +221,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/huandu/xstrings v1.4.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect @@ -300,12 +300,12 @@ require ( github.com/selectel/go-selvpcclient/v3 v3.1.1 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/softlayer/softlayer-go v1.1.5 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898 // indirect @@ -317,7 +317,7 @@ require ( github.com/transip/gotransip/v6 v6.23.0 // indirect github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect - github.com/vultr/govultr/v2 v2.17.2 // indirect + github.com/vultr/govultr/v3 v3.9.0 // indirect github.com/yandex-cloud/go-genproto v0.0.0-20240318083951-4fe6125f286e // indirect github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect @@ -338,10 +338,10 @@ require ( go.uber.org/ratelimit v0.3.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/term v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/term v0.23.0 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect diff --git a/go.sum b/go.sum index d2625130b..927bfbf39 100644 --- a/go.sum +++ b/go.sum @@ -283,8 +283,8 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 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= @@ -302,8 +302,8 @@ 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.17.4 h1:h0nePd3ObP6o7kAkndtpTzCw8shOZuWckNYeUQwo36Q= -github.com/go-acme/lego/v4 v4.17.4/go.mod h1:dU94SvPNqimEeb7EVilGGSnS0nU1O5Exir0pQ4QFL4U= +github.com/go-acme/lego/v4 v4.18.0 h1:2hH8KcdRBSb+p5o9VZIm61GAOXYALgILUCSs1Q+OYsk= +github.com/go-acme/lego/v4 v4.18.0/go.mod h1:Blkg3izvXpl3zxk7WKngIuwR2I/hvYVP3vRnvgBp7m8= 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= @@ -553,8 +553,8 @@ github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= @@ -922,8 +922,8 @@ github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnj github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -950,8 +950,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B 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/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/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 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= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1016,8 +1016,8 @@ github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltas github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4= github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70 h1:I+oBnV0orhmasb87yaX54tOAfqrV9+yKoQ1Cum5mq8w= github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70/go.mod h1:zQB3w+df4hryDEqBorGyA1DwPJ86LfKIASNLFuj6CuI= -github.com/traefik/paerser v0.2.0 h1:zqCLGSXoNlcBd+mzqSCLjon/I6phqIjeJL2xFB2ysgQ= -github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc= +github.com/traefik/paerser v0.2.1 h1:LFgeak1NmjEHF53c9ENdXdL1UMkF/lD5t+7Evsz4hH4= +github.com/traefik/paerser v0.2.1/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24= github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E= github.com/traefik/yaegi v0.16.1/go.mod h1:4eVhbPb3LnD2VigQjhYbEJ69vDRFdT2HQNrXx8eEwUY= github.com/transip/gotransip/v6 v6.23.0 h1:PsTdjortrEZ8IFFifEryzjVjOy9SgK4ahlnhKBBIQgA= @@ -1053,8 +1053,8 @@ github.com/vulcand/oxy/v2 v2.0.0 h1:V+scHhd2xBjO8ojBRgxCM+OdZxRA/YTs8M70w5tdNy8= github.com/vulcand/oxy/v2 v2.0.0/go.mod h1:uIAz3sYafO7i+V3SC8oDlMn/lt1i9aWcyXuXqVswKzE= github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50= github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA= -github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= -github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/vultr/govultr/v3 v3.9.0 h1:63V/22mpfquRA5DenJ9EF0VozHg0k+X4dhUWcDXHPyc= +github.com/vultr/govultr/v3 v3.9.0/go.mod h1:Rd8ebpXm7jxH3MDmhnEs+zrlYW212ouhx+HeUMfHm2o= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -1161,8 +1161,8 @@ 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.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 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= @@ -1253,8 +1253,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1322,8 +1322,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1333,8 +1333,8 @@ 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.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 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= @@ -1348,8 +1348,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/integration/access_log_test.go b/integration/access_log_test.go index b5e4ec1ef..e1c4003e9 100644 --- a/integration/access_log_test.go +++ b/integration/access_log_test.go @@ -606,10 +606,8 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() { func (s *AccessLogSuite) TestAccessLogDisabledForInternals() { ensureWorkingDirectoryIsClean() - file := s.adaptFile("fixtures/access_log/access_log_ping.toml", struct{}{}) - // Start Traefik. - s.traefikCmd(withConfigFile(file)) + s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml")) defer func() { traefikLog, err := os.ReadFile(traefikTestLogFile) @@ -619,7 +617,7 @@ func (s *AccessLogSuite) TestAccessLogDisabledForInternals() { // waitForTraefik makes at least one call to the rawdata api endpoint, // but the logs for this endpoint are ignored in checkAccessLogOutput. - s.waitForTraefik("customPing") + s.waitForTraefik("service3") s.checkStatsForLogFile() @@ -636,8 +634,9 @@ func (s *AccessLogSuite) TestAccessLogDisabledForInternals() { require.NoError(s.T(), err) // Make some requests on the custom ping router. - req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/ping", nil) + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8010/ping", nil) require.NoError(s.T(), err) + req.Host = "ping.docker.local" err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) require.NoError(s.T(), err) @@ -649,6 +648,25 @@ func (s *AccessLogSuite) TestAccessLogDisabledForInternals() { require.Equal(s.T(), 0, count) + // Make some requests on the custom ping router in error. + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8010/ping-error", nil) + require.NoError(s.T(), err) + req.Host = "ping-error.docker.local" + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.BodyContains("X-Forwarded-Host: ping-error.docker.local")) + require.NoError(s.T(), err) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.BodyContains("X-Forwarded-Host: ping-error.docker.local")) + require.NoError(s.T(), err) + + // Here we verify that the remove of observability doesn't break the metrics for the error page service. + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil) + require.NoError(s.T(), err) + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("service3")) + require.NoError(s.T(), err) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyNotContains("service=\"ping")) + require.NoError(s.T(), err) + // Verify no other Traefik problems. s.checkNoOtherTraefikProblems() } diff --git a/integration/conformance-reports/v1.1.0/experimental-v3.1-default-report.yaml b/integration/conformance-reports/v1.1.0/experimental-v3.1-default-report.yaml index e668037eb..64047e5b2 100644 --- a/integration/conformance-reports/v1.1.0/experimental-v3.1-default-report.yaml +++ b/integration/conformance-reports/v1.1.0/experimental-v3.1-default-report.yaml @@ -53,3 +53,11 @@ profiles: - HTTPRouteRequestTimeout name: GATEWAY-HTTP summary: Core tests succeeded. Extended tests succeeded. +- core: + result: success + statistics: + Failed: 0 + Passed: 11 + Skipped: 0 + name: GATEWAY-TLS + summary: Core tests succeeded. diff --git a/integration/fixtures/access_log/access_log_base.toml b/integration/fixtures/access_log/access_log_base.toml index 8d954450e..5a88f75e9 100644 --- a/integration/fixtures/access_log/access_log_base.toml +++ b/integration/fixtures/access_log/access_log_base.toml @@ -22,10 +22,17 @@ address = ":8008" [entryPoints.preflight] address = ":8009" + [entryPoints.ping] + address = ":8010" [api] insecure = true +[ping] + +[metrics] + [metrics.prometheus] + [providers] [providers.docker] exposedByDefault = false diff --git a/integration/fixtures/access_log/access_log_ping.toml b/integration/fixtures/access_log/access_log_ping.toml deleted file mode 100644 index 4e85b93bc..000000000 --- a/integration/fixtures/access_log/access_log_ping.toml +++ /dev/null @@ -1,30 +0,0 @@ -[global] - checkNewVersion = false - sendAnonymousUsage = false - -[log] - level = "ERROR" - filePath = "traefik.log" - -[accessLog] - filePath = "access.log" - -[entryPoints] - [entryPoints.web] - address = ":8000" - -[api] - insecure = true - -[ping] - -[providers] - [providers.file] - filename = "{{ .SelfFilename }}" - -## dynamic configuration ## -[http.routers] - [http.routers.customPing] - entryPoints = ["web"] - rule = "PathPrefix(`/ping`)" - service = "ping@internal" diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index 30038945a..96816d8a4 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -2506,6 +2506,11 @@ spec: Default value is -1, which means unlimited size. format: int64 type: integer + mirrorBody: + description: |- + MirrorBody defines whether the body of the request should be mirrored. + Default value is true. + type: boolean mirrors: description: Mirrors defines the list of mirrors where Traefik will duplicate the traffic. diff --git a/integration/fixtures/mirror.toml b/integration/fixtures/mirror.toml index 4c99f83b8..b28878480 100644 --- a/integration/fixtures/mirror.toml +++ b/integration/fixtures/mirror.toml @@ -28,6 +28,10 @@ service = "mirrorWithMaxBody" rule = "Path(`/whoamiWithMaxBody`)" + [http.routers.router3] + service = "mirrorWithoutBody" + rule = "Path(`/whoamiWithoutBody`)" + [http.services] [http.services.mirror.mirroring] @@ -49,6 +53,16 @@ name = "mirror2" percent = 50 + [http.services.mirrorWithoutBody.mirroring] + service = "service1" + mirrorBody = false + [[http.services.mirrorWithoutBody.mirroring.mirrors]] + name = "mirror1" + percent = 10 + [[http.services.mirrorWithoutBody.mirroring.mirrors]] + name = "mirror2" + percent = 50 + [http.services.service1.loadBalancer] [[http.services.service1.loadBalancer.servers]] diff --git a/integration/k8s_conformance_test.go b/integration/k8s_conformance_test.go index 70ec948ea..9316715af 100644 --- a/integration/k8s_conformance_test.go +++ b/integration/k8s_conformance_test.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "slices" + "strings" "testing" "time" @@ -193,7 +194,11 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { Version: *k8sConformanceTraefikVersion, Contact: []string{"@traefik/maintainers"}, }, - ConformanceProfiles: sets.New(ksuite.GatewayHTTPConformanceProfileName, ksuite.GatewayGRPCConformanceProfileName), + ConformanceProfiles: sets.New( + ksuite.GatewayHTTPConformanceProfileName, + ksuite.GatewayGRPCConformanceProfileName, + ksuite.GatewayTLSConformanceProfileName, + ), SupportedFeatures: sets.New( features.SupportGateway, features.SupportGatewayPort8080, @@ -207,6 +212,7 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { features.SupportHTTPRoutePathRewrite, features.SupportHTTPRoutePathRedirect, features.SupportHTTPRouteResponseHeaderModification, + features.SupportTLSRoute, ), }) require.NoError(s.T(), err) @@ -224,6 +230,11 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { // TODO: to publish this report automatically, we have to figure out how to handle the date diff. report.Date = "-" + // Ordering profile reports for the serialized report to be comparable. + slices.SortFunc(report.ProfileReports, func(a, b v1.ProfileReport) int { + return strings.Compare(a.Name, b.Name) + }) + rawReport, err := yaml.Marshal(report) require.NoError(s.T(), err) s.T().Logf("Conformance report:\n%s", string(rawReport)) diff --git a/integration/resources/compose/access_log.yml b/integration/resources/compose/access_log.yml index c9cf06db7..c5be8f9e4 100644 --- a/integration/resources/compose/access_log.yml +++ b/integration/resources/compose/access_log.yml @@ -94,3 +94,21 @@ services: traefik.http.routers.rt-preflightCORS.middlewares: preflightCORS traefik.http.middlewares.preflightCORS.headers.accessControlAllowMethods: OPTIONS, GET traefik.http.services.preflightCORS.loadbalancer.server.port: 80 + + ping: + image: traefik/whoami + labels: + traefik.enable: true + traefik.http.routers.ping.entryPoints: ping + traefik.http.routers.ping.rule: PathPrefix(`/ping`) + traefik.http.routers.ping.service: ping@internal + + traefik.http.routers.ping-error.entryPoints: ping + traefik.http.routers.ping-error.rule: PathPrefix(`/ping-error`) + traefik.http.routers.ping-error.middlewares: errors, basicauth + traefik.http.routers.ping-error.service: ping@internal + traefik.http.middlewares.basicauth.basicauth.users: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + traefik.http.middlewares.errors.errors.status: 401 + traefik.http.middlewares.errors.errors.service: service3 + traefik.http.middlewares.errors.errors.query: / + traefik.http.services.service3.loadbalancer.server.port: 80 diff --git a/integration/simple_test.go b/integration/simple_test.go index 018a4b67a..9aae5c1b2 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -1004,8 +1004,13 @@ func (s *SimpleSuite) TestMirrorWithBody() { _, err = rand.Read(body5) require.NoError(s.T(), err) - verifyBody := func(req *http.Request) { + // forceOkResponse is used to avoid errors when Content-Length is set but no body is received + verifyBody := func(req *http.Request, canBodyBeEmpty bool) (forceOkResponse bool) { b, _ := io.ReadAll(req.Body) + if canBodyBeEmpty && req.Header.Get("NoBody") == "true" { + require.Empty(s.T(), b) + return true + } switch req.Header.Get("Size") { case "20": require.Equal(s.T(), body20, b) @@ -1014,20 +1019,25 @@ func (s *SimpleSuite) TestMirrorWithBody() { default: s.T().Fatal("Size header not present") } + return false } main := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - verifyBody(req) + verifyBody(req, false) atomic.AddInt32(&count, 1) })) mirror1 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - verifyBody(req) + if verifyBody(req, true) { + rw.WriteHeader(http.StatusOK) + } atomic.AddInt32(&countMirror1, 1) })) mirror2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - verifyBody(req) + if verifyBody(req, true) { + rw.WriteHeader(http.StatusOK) + } atomic.AddInt32(&countMirror2, 1) })) @@ -1104,6 +1114,28 @@ func (s *SimpleSuite) TestMirrorWithBody() { assert.Equal(s.T(), int32(10), countTotal) assert.Equal(s.T(), int32(0), val1) assert.Equal(s.T(), int32(0), val2) + + atomic.StoreInt32(&count, 0) + atomic.StoreInt32(&countMirror1, 0) + atomic.StoreInt32(&countMirror2, 0) + + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoamiWithoutBody", bytes.NewBuffer(body20)) + require.NoError(s.T(), err) + req.Header.Set("Size", "20") + req.Header.Set("NoBody", "true") + for range 10 { + response, err := http.DefaultClient.Do(req) + require.NoError(s.T(), err) + assert.Equal(s.T(), http.StatusOK, response.StatusCode) + } + + countTotal = atomic.LoadInt32(&count) + val1 = atomic.LoadInt32(&countMirror1) + val2 = atomic.LoadInt32(&countMirror2) + + assert.Equal(s.T(), int32(10), countTotal) + assert.Equal(s.T(), int32(1), val1) + assert.Equal(s.T(), int32(5), val2) } func (s *SimpleSuite) TestMirrorCanceled() { diff --git a/integration/testdata/rawdata-gateway.json b/integration/testdata/rawdata-gateway.json index d4dd467b0..15ee8b6ce 100644 --- a/integration/testdata/rawdata-gateway.json +++ b/integration/testdata/rawdata-gateway.json @@ -34,7 +34,7 @@ "entryPoints": [ "web" ], - "service": "default-http-app-1-my-gateway-web-0-wrr", + "service": "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", "rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)", "ruleSyntax": "v3", "priority": 100008, @@ -47,7 +47,7 @@ "entryPoints": [ "websecure" ], - "service": "default-http-app-1-my-https-gateway-websecure-0-wrr", + "service": "default-http-app-1-my-https-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr", "rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)", "ruleSyntax": "v3", "priority": 100008, @@ -96,7 +96,7 @@ "dashboard@internal" ] }, - "default-http-app-1-my-gateway-web-0-wrr@kubernetesgateway": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr@kubernetesgateway": { "weighted": { "services": [ { @@ -110,7 +110,7 @@ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06@kubernetesgateway" ] }, - "default-http-app-1-my-https-gateway-websecure-0-wrr@kubernetesgateway": { + "default-http-app-1-my-https-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr@kubernetesgateway": { "weighted": { "services": [ { @@ -150,11 +150,11 @@ } }, "tcpRouters": { - "default-tcp-app-1-my-tcp-gateway-footcp@kubernetesgateway": { + "default-tcp-app-1-my-tcp-gateway-footcp-0-e3b0c44298fc1c149afb@kubernetesgateway": { "entryPoints": [ "footcp" ], - "service": "default-tcp-app-1-my-tcp-gateway-footcp-wrr-0", + "service": "default-tcp-app-1-my-tcp-gateway-footcp-0-e3b0c44298fc1c149afb-wrr", "rule": "HostSNI(`*`)", "ruleSyntax": "v3", "priority": -1, @@ -163,11 +163,11 @@ "footcp" ] }, - "default-tcp-app-1-my-tls-gateway-footlsterminate@kubernetesgateway": { + "default-tcp-app-1-my-tls-gateway-footlsterminate-0-e3b0c44298fc1c149afb@kubernetesgateway": { "entryPoints": [ "footlsterminate" ], - "service": "default-tcp-app-1-my-tls-gateway-footlsterminate-wrr-0", + "service": "default-tcp-app-1-my-tls-gateway-footlsterminate-0-e3b0c44298fc1c149afb-wrr", "rule": "HostSNI(`*`)", "ruleSyntax": "v3", "priority": -1, @@ -179,11 +179,11 @@ "footlsterminate" ] }, - "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26@kubernetesgateway": { + "default-tls-app-1-my-tls-gateway-footlspassthrough-0-e3b0c44298fc1c149afb@kubernetesgateway": { "entryPoints": [ "footlspassthrough" ], - "service": "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26-wrr-0", + "service": "default-tls-app-1-my-tls-gateway-footlspassthrough-0-e3b0c44298fc1c149afb-wrr", "rule": "HostSNI(`foo.bar`)", "ruleSyntax": "v3", "priority": 18, @@ -197,7 +197,7 @@ } }, "tcpServices": { - "default-tcp-app-1-my-tcp-gateway-footcp-wrr-0@kubernetesgateway": { + "default-tcp-app-1-my-tcp-gateway-footcp-0-e3b0c44298fc1c149afb-wrr@kubernetesgateway": { "weighted": { "services": [ { @@ -208,10 +208,10 @@ }, "status": "enabled", "usedBy": [ - "default-tcp-app-1-my-tcp-gateway-footcp@kubernetesgateway" + "default-tcp-app-1-my-tcp-gateway-footcp-0-e3b0c44298fc1c149afb@kubernetesgateway" ] }, - "default-tcp-app-1-my-tls-gateway-footlsterminate-wrr-0@kubernetesgateway": { + "default-tcp-app-1-my-tls-gateway-footlsterminate-0-e3b0c44298fc1c149afb-wrr@kubernetesgateway": { "weighted": { "services": [ { @@ -222,10 +222,10 @@ }, "status": "enabled", "usedBy": [ - "default-tcp-app-1-my-tls-gateway-footlsterminate@kubernetesgateway" + "default-tcp-app-1-my-tls-gateway-footlsterminate-0-e3b0c44298fc1c149afb@kubernetesgateway" ] }, - "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26-wrr-0@kubernetesgateway": { + "default-tls-app-1-my-tls-gateway-footlspassthrough-0-e3b0c44298fc1c149afb-wrr@kubernetesgateway": { "weighted": { "services": [ { @@ -236,7 +236,7 @@ }, "status": "enabled", "usedBy": [ - "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26@kubernetesgateway" + "default-tls-app-1-my-tls-gateway-footlspassthrough-0-e3b0c44298fc1c149afb@kubernetesgateway" ] }, "default-whoamitcp-8080@kubernetesgateway": { diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 48b2e763c..d347df81e 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -81,6 +81,7 @@ type RouterTLSConfig struct { // Mirroring holds the Mirroring configuration. type Mirroring struct { Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` + MirrorBody *bool `json:"mirrorBody,omitempty" toml:"mirrorBody,omitempty" yaml:"mirrorBody,omitempty" export:"true"` MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty" export:"true"` Mirrors []MirrorService `json:"mirrors,omitempty" toml:"mirrors,omitempty" yaml:"mirrors,omitempty" export:"true"` HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` @@ -88,6 +89,8 @@ type Mirroring struct { // SetDefaults Default values for a WRRService. func (m *Mirroring) SetDefaults() { + defaultMirrorBody := true + m.MirrorBody = &defaultMirrorBody var defaultMaxBodySize int64 = -1 m.MaxBodySize = &defaultMaxBodySize } diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index a6ef36420..95a4c803a 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -967,6 +967,11 @@ func (in *MirrorService) DeepCopy() *MirrorService { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Mirroring) DeepCopyInto(out *Mirroring) { *out = *in + if in.MirrorBody != nil { + in, out := &in.MirrorBody, &out.MirrorBody + *out = new(bool) + **out = **in + } if in.MaxBodySize != nil { in, out := &in.MaxBodySize, &out.MaxBodySize *out = new(int64) diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index f36bd50cf..36a47f4dd 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -212,6 +212,9 @@ type Tracing struct { func (t *Tracing) SetDefaults() { t.ServiceName = "traefik" t.SampleRate = 1.0 + + t.OTLP = &opentelemetry.Config{} + t.OTLP.SetDefaults() } // Providers contains providers configuration. diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go index ef3f3be6c..19dc43d6c 100644 --- a/pkg/middlewares/accesslog/logdata.go +++ b/pkg/middlewares/accesslog/logdata.go @@ -77,6 +77,11 @@ const ( TLSCipher = "TLSCipher" // TLSClientSubject is the string representation of the TLS client certificate's Subject. TLSClientSubject = "TLSClientSubject" + + // TraceID is the consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string. + TraceID = "TraceId" + // SpanID is the unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. + SpanID = "SpanId" ) // These are written out in the default case when no config is provided to specify keys of interest. diff --git a/pkg/middlewares/capture/capture.go b/pkg/middlewares/capture/capture.go index b7e182117..04e411a20 100644 --- a/pkg/middlewares/capture/capture.go +++ b/pkg/middlewares/capture/capture.go @@ -43,9 +43,21 @@ const capturedData key = "capturedData" // It satisfies the alice.Constructor type. func Wrap(next http.Handler) (http.Handler, error) { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - c := &Capture{} - newRW, newReq := c.renew(rw, req) - next.ServeHTTP(newRW, newReq) + capt, err := FromContext(req.Context()) + if err != nil { + c := &Capture{} + newRW, newReq := c.renew(rw, req) + next.ServeHTTP(newRW, newReq) + return + } + + if capt.NeedsReset(rw) { + newRW, newReq := capt.renew(rw, req) + next.ServeHTTP(newRW, newReq) + return + } + + next.ServeHTTP(rw, req) }), nil } diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go index 0de455b92..4849f25b2 100644 --- a/pkg/middlewares/observability/entrypoint.go +++ b/pkg/middlewares/observability/entrypoint.go @@ -11,6 +11,7 @@ import ( "github.com/containous/alice" "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" + "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -69,6 +70,12 @@ func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) e.tracer.CaptureServerRequest(span, req) + if logData := accesslog.GetLogData(req); logData != nil { + spanContext := span.SpanContext() + logData.Core[accesslog.TraceID] = spanContext.TraceID().String() + logData.Core[accesslog.SpanID] = spanContext.SpanID().String() + } + recorder := newStatusCodeRecorder(rw, http.StatusOK) e.next.ServeHTTP(recorder, req) diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go index f32c052c8..3e7a90870 100644 --- a/pkg/middlewares/observability/entrypoint_test.go +++ b/pkg/middlewares/observability/entrypoint_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/otel/attribute" @@ -181,3 +182,27 @@ func TestEntryPointMiddleware_metrics(t *testing.T) { }) } } + +func TestEntryPointMiddleware_tracingInfoIntoLog(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/", http.NoBody) + req = req.WithContext( + context.WithValue( + req.Context(), + accesslog.DataTableKey, + &accesslog.LogData{Core: accesslog.CoreLogData{}}, + ), + ) + + next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {}) + + tracer := &mockTracer{} + + handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{}, []string{}, []string{}), nil, "test", next) + handler.ServeHTTP(httptest.NewRecorder(), req) + + expectedSpanCtx := tracer.spans[0].SpanContext() + + logData := accesslog.GetLogData(req) + assert.Equal(t, expectedSpanCtx.TraceID().String(), logData.Core[accesslog.TraceID]) + assert.Equal(t, expectedSpanCtx.SpanID().String(), logData.Core[accesslog.SpanID]) +} diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index d81a02058..8f00ec238 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -290,6 +290,7 @@ func (c configBuilder) buildMirroring(ctx context.Context, tService *traefikv1al Mirroring: &dynamic.Mirroring{ Service: fullNameMain, Mirrors: mirrorServices, + MirrorBody: tService.Spec.Mirroring.MirrorBody, MaxBodySize: tService.Spec.Mirroring.MaxBodySize, }, } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go index ed1955a8a..cdbd95b0e 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go @@ -53,6 +53,9 @@ type TraefikServiceSpec struct { type Mirroring struct { LoadBalancerSpec `json:",inline"` + // MirrorBody defines whether the body of the request should be mirrored. + // Default value is true. + MirrorBody *bool `json:"mirrorBody,omitempty"` // MaxBodySize defines the maximum size allowed for the body of the request. // If the body is larger, the request is not mirrored. // Default value is -1, which means unlimited size. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go index caa1bf9ec..466cc7577 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go @@ -972,6 +972,11 @@ func (in *MirrorService) DeepCopy() *MirrorService { func (in *Mirroring) DeepCopyInto(out *Mirroring) { *out = *in in.LoadBalancerSpec.DeepCopyInto(&out.LoadBalancerSpec) + if in.MirrorBody != nil { + in, out := &in.MirrorBody, &out.MirrorBody + *out = new(bool) + **out = **in + } if in.MaxBodySize != nil { in, out := &in.MaxBodySize, &out.MaxBodySize *out = new(int64) diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_wrong_service_port.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_wrong_service_port.yml index 6de20a14a..ee629a6db 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_wrong_service_port.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_wrong_service_port.yml @@ -32,6 +32,10 @@ metadata: name: TCP-app-1 namespace: default spec: + parentRefs: + - name: my-gateway + kind: Gateway + group: gateway.networking.k8s.io rules: - backendRefs: - name: whoami diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml index c0c472a55..5d296c248 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml @@ -36,16 +36,16 @@ spec: group: "" allowedRoutes: kinds: - - kind: TCPRoute + - kind: TLSRoute group: gateway.networking.k8s.io namespaces: from: Same --- -kind: TCPRoute +kind: TLSRoute apiVersion: gateway.networking.k8s.io/v1alpha2 metadata: - name: tcp-app-1 + name: tls-app-1 namespace: default spec: parentRefs: diff --git a/pkg/provider/kubernetes/gateway/grpcroute.go b/pkg/provider/kubernetes/gateway/grpcroute.go index baee82876..9447943a0 100644 --- a/pkg/provider/kubernetes/gateway/grpcroute.go +++ b/pkg/provider/kubernetes/gateway/grpcroute.go @@ -216,7 +216,7 @@ func (p *Provider) loadGRPCService(conf *dynamic.Configuration, routeKey string, } func (p *Provider) loadGRPCBackendRef(route *gatev1.GRPCRoute, backendRef gatev1.GRPCBackendRef) (string, *dynamic.Service, *metav1.Condition) { - kind := ptr.Deref(backendRef.Kind, "Service") + kind := ptr.Deref(backendRef.Kind, kindService) group := groupCore if backendRef.Group != nil && *backendRef.Group != "" { @@ -230,7 +230,7 @@ func (p *Provider) loadGRPCBackendRef(route *gatev1.GRPCRoute, backendRef gatev1 serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name)) - if group != groupCore || kind != "Service" { + if group != groupCore || kind != kindService { return serviceName, nil, &metav1.Condition{ Type: string(gatev1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -241,7 +241,7 @@ func (p *Provider) loadGRPCBackendRef(route *gatev1.GRPCRoute, backendRef gatev1 } } - if err := p.isReferenceGranted(groupGateway, kindGRPCRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { + if err := p.isReferenceGranted(kindGRPCRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { return serviceName, nil, &metav1.Condition{ Type: string(gatev1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go index ed4efc659..685a6fbfd 100644 --- a/pkg/provider/kubernetes/gateway/httproute.go +++ b/pkg/provider/kubernetes/gateway/httproute.go @@ -158,7 +158,7 @@ func (p *Provider) loadHTTPRoute(ctx context.Context, listener gatewayListener, default: var serviceCondition *metav1.Condition - router.Service, serviceCondition = p.loadService(conf, routeKey, routeRule, route) + router.Service, serviceCondition = p.loadWRRService(conf, routerName, routeRule, route) if serviceCondition != nil { condition = *serviceCondition } @@ -173,7 +173,7 @@ func (p *Provider) loadHTTPRoute(ctx context.Context, listener gatewayListener, return conf, condition } -func (p *Provider) loadService(conf *dynamic.Configuration, routeKey string, routeRule gatev1.HTTPRouteRule, route *gatev1.HTTPRoute) (string, *metav1.Condition) { +func (p *Provider) loadWRRService(conf *dynamic.Configuration, routeKey string, routeRule gatev1.HTTPRouteRule, route *gatev1.HTTPRoute) (string, *metav1.Condition) { name := routeKey + "-wrr" if _, ok := conf.HTTP.Services[name]; ok { return name, nil @@ -182,7 +182,7 @@ func (p *Provider) loadService(conf *dynamic.Configuration, routeKey string, rou var wrr dynamic.WeightedRoundRobin var condition *metav1.Condition for _, backendRef := range routeRule.BackendRefs { - svcName, svc, errCondition := p.loadHTTPService(route, backendRef) + svcName, svc, errCondition := p.loadService(route, backendRef) weight := ptr.To(int(ptr.Deref(backendRef.Weight, 1))) if errCondition != nil { condition = errCondition @@ -208,10 +208,10 @@ func (p *Provider) loadService(conf *dynamic.Configuration, routeKey string, rou return name, condition } -// loadHTTPService returns a dynamic.Service config corresponding to the given gatev1.HTTPBackendRef. +// loadService returns a dynamic.Service config corresponding to the given gatev1.HTTPBackendRef. // Note that the returned dynamic.Service config can be nil (for cross-provider, internal services, and backendFunc). -func (p *Provider) loadHTTPService(route *gatev1.HTTPRoute, backendRef gatev1.HTTPBackendRef) (string, *dynamic.Service, *metav1.Condition) { - kind := ptr.Deref(backendRef.Kind, "Service") +func (p *Provider) loadService(route *gatev1.HTTPRoute, backendRef gatev1.HTTPBackendRef) (string, *dynamic.Service, *metav1.Condition) { + kind := ptr.Deref(backendRef.Kind, kindService) group := groupCore if backendRef.Group != nil && *backendRef.Group != "" { @@ -225,7 +225,7 @@ func (p *Provider) loadHTTPService(route *gatev1.HTTPRoute, backendRef gatev1.HT serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name)) - if err := p.isReferenceGranted(groupGateway, kindHTTPRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { + if err := p.isReferenceGranted(kindHTTPRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { return serviceName, nil, &metav1.Condition{ Type: string(gatev1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -236,7 +236,7 @@ func (p *Provider) loadHTTPService(route *gatev1.HTTPRoute, backendRef gatev1.HT } } - if group != groupCore || kind != "Service" { + if group != groupCore || kind != kindService { name, service, err := p.loadHTTPBackendRef(namespace, backendRef) if err != nil { return serviceName, nil, &metav1.Condition{ diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 7a65ea977..b8c878627 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -47,6 +47,7 @@ const ( kindGRPCRoute = "GRPCRoute" kindTCPRoute = "TCPRoute" kindTLSRoute = "TLSRoute" + kindService = "Service" ) // Provider holds configurations of the provider. @@ -376,11 +377,19 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) *dynamic.C } } - gatewayStatus, err := p.makeGatewayStatus(gateway, listeners, addresses) - if err != nil { + gatewayStatus, errConditions := p.makeGatewayStatus(gateway, listeners, addresses) + if len(errConditions) > 0 { + messages := map[string]struct{}{} + for _, condition := range errConditions { + messages[condition.Message] = struct{}{} + } + var conditionsErr error + for message := range messages { + conditionsErr = multierror.Append(conditionsErr, errors.New(message)) + } logger.Error(). - Err(err). - Msg("Unable to create Gateway status") + Err(conditionsErr). + Msg("Gateway Not Accepted") } if err = p.client.UpdateGatewayStatus(ctx, ktypes.NamespacedName{Name: gateway.Name, Namespace: gateway.Namespace}, gatewayStatus); err != nil { @@ -576,7 +585,7 @@ func (p *Provider) loadGatewayListeners(ctx context.Context, gateway *gatev1.Gat certificateNamespace = string(*certificateRef.Namespace) } - if err := p.isReferenceGranted(groupGateway, kindGateway, gateway.Namespace, groupCore, "Secret", string(certificateRef.Name), certificateNamespace); err != nil { + if err := p.isReferenceGranted(kindGateway, gateway.Namespace, groupCore, "Secret", string(certificateRef.Name), certificateNamespace); err != nil { gatewayListeners[i].Status.Conditions = append(gatewayListeners[i].Status.Conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -631,10 +640,10 @@ func (p *Provider) loadGatewayListeners(ctx context.Context, gateway *gatev1.Gat return gatewayListeners } -func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewayListener, addresses []gatev1.GatewayStatusAddress) (gatev1.GatewayStatus, error) { +func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewayListener, addresses []gatev1.GatewayStatusAddress) (gatev1.GatewayStatus, []metav1.Condition) { gatewayStatus := gatev1.GatewayStatus{Addresses: addresses} - var result error + var errorConditions []metav1.Condition for _, listener := range listeners { if len(listener.Status.Conditions) == 0 { listener.Status.Conditions = append(listener.Status.Conditions, @@ -669,14 +678,11 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa continue } - for _, condition := range listener.Status.Conditions { - result = multierror.Append(result, errors.New(condition.Message)) - } - + errorConditions = append(errorConditions, listener.Status.Conditions...) gatewayStatus.Listeners = append(gatewayStatus.Listeners, *listener.Status) } - if result != nil { + if len(errorConditions) > 0 { // GatewayConditionReady "Ready", GatewayConditionReason "ListenersNotValid" gatewayStatus.Conditions = append(gatewayStatus.Conditions, metav1.Condition{ Type: string(gatev1.GatewayConditionAccepted), @@ -687,7 +693,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listeners []gatewa Message: "All Listeners must be valid", }) - return gatewayStatus, result + return gatewayStatus, errorConditions } gatewayStatus.Conditions = append(gatewayStatus.Conditions, @@ -783,7 +789,7 @@ func (p *Provider) entryPointName(port gatev1.PortNumber, protocol gatev1.Protoc return "", fmt.Errorf("no matching entryPoint for port %d and protocol %q", port, protocol) } -func (p *Provider) isReferenceGranted(fromGroup, fromKind, fromNamespace, toGroup, toKind, toName, toNamespace string) error { +func (p *Provider) isReferenceGranted(fromKind, fromNamespace, toGroup, toKind, toName, toNamespace string) error { if toNamespace == fromNamespace { return nil } @@ -793,7 +799,7 @@ func (p *Provider) isReferenceGranted(fromGroup, fromKind, fromNamespace, toGrou return fmt.Errorf("listing ReferenceGrant: %w", err) } - refGrants = filterReferenceGrantsFrom(refGrants, fromGroup, fromKind, fromNamespace) + refGrants = filterReferenceGrantsFrom(refGrants, groupGateway, fromKind, fromNamespace) refGrants = filterReferenceGrantsTo(refGrants, toGroup, toKind, toName) if len(refGrants) == 0 { return errors.New("missing ReferenceGrant") diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 414a85f19..79750f4c8 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -181,7 +181,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, { - desc: "Empty caused by multi ports service with wrong TargetPort", + desc: "Router with service in error caused by wrong TargetPort", entryPoints: map[string]Entrypoint{"web": { Address: ":80", }}, @@ -201,7 +201,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -209,7 +209,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -570,7 +570,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -578,7 +578,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -665,7 +665,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-1-my-gateway-websecure-0-wrr", + Service: "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -674,7 +674,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-websecure-0-wrr": { + "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -736,7 +736,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-66e726cd8903b49727ae": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-66e726cd8903b49727ae-wrr", Rule: "(Host(`foo.com`) || Host(`bar.com`)) && PathPrefix(`/`)", Priority: 9, RuleSyntax: "v3", @@ -744,7 +744,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-66e726cd8903b49727ae-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -797,7 +797,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-baa117c0219e3878749f": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-baa117c0219e3878749f-wrr", Rule: "(Host(`foo.com`) || HostRegexp(`^[a-z0-9-\\.]+\\.bar\\.com$`)) && PathPrefix(`/`)", Priority: 11, RuleSyntax: "v3", @@ -805,7 +805,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-baa117c0219e3878749f-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -858,7 +858,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-45eba2eaf40ac792e036": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-45eba2eaf40ac792e036-wrr", Rule: "(Host(`foo.com`) || HostRegexp(`^[a-z0-9-\\.]+\\.foo\\.com$`)) && PathPrefix(`/`)", Priority: 11, RuleSyntax: "v3", @@ -866,7 +866,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-45eba2eaf40ac792e036-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -922,19 +922,19 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100009, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", }, "default-http-app-1-my-gateway-web-1-d737b4933fa88e68ab8a": { EntryPoints: []string{"web"}, Rule: "Host(`foo.com`) && Path(`/bir`)", Priority: 100008, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-1-wrr", + Service: "default-http-app-1-my-gateway-web-1-d737b4933fa88e68ab8a-wrr", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -944,7 +944,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-web-1-wrr": { + "default-http-app-1-my-gateway-web-1-d737b4933fa88e68ab8a-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1016,12 +1016,12 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1099,14 +1099,14 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-http-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-http-web-0-wrr", + Service: "default-http-app-1-my-gateway-http-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", }, "default-http-app-1-my-gateway-https-websecure-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-1-my-gateway-https-websecure-0-wrr", + Service: "default-http-app-1-my-gateway-https-websecure-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -1115,7 +1115,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-http-web-0-wrr": { + "default-http-app-1-my-gateway-http-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1125,7 +1125,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-https-websecure-0-wrr": { + "default-http-app-1-my-gateway-https-websecure-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1192,14 +1192,14 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", }, "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-1-my-gateway-websecure-0-wrr", + Service: "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -1208,7 +1208,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1218,7 +1218,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-websecure-0-wrr": { + "default-http-app-1-my-gateway-websecure-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1283,26 +1283,26 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && (Path(`/bar`) || PathPrefix(`/bar/`)) && Header(`my-header`,`foo`) && Header(`my-header2`,`bar`)", Priority: 10610, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-6cf37fa71907768d925c-wrr", }, "default-http-app-1-my-gateway-web-2-d23f7039bc8036fb918c": { EntryPoints: []string{"web"}, Rule: "Host(`foo.com`) && PathRegexp(`^/buzz/[0-9]+$`)", Priority: 11408, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-2-wrr", + Service: "default-http-app-1-my-gateway-web-2-d23f7039bc8036fb918c-wrr", }, "default-http-app-1-my-gateway-web-1-aaba0f24fd26e1ca2276": { EntryPoints: []string{"web"}, Rule: "Host(`foo.com`) && Path(`/bar`) && Header(`my-header`,`bar`)", Priority: 100109, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-1-wrr", + Service: "default-http-app-1-my-gateway-web-1-aaba0f24fd26e1ca2276-wrr", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-6cf37fa71907768d925c-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1312,7 +1312,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-web-2-wrr": { + "default-http-app-1-my-gateway-web-2-d23f7039bc8036fb918c-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1322,7 +1322,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-web-1-wrr": { + "default-http-app-1-my-gateway-web-1-aaba0f24fd26e1ca2276-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1378,12 +1378,12 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && (Path(`/foo`) || PathPrefix(`/foo/`)) && Method(`GET`)", Priority: 11408, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-74ad70a7cf090becdd3c-wrr", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-74ad70a7cf090becdd3c-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1439,12 +1439,12 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && (Path(`/foo`) || PathPrefix(`/foo/`)) && Query(`foo`,`bar`) && QueryRegexp(`baz`,`buz`)", Priority: 10428, RuleSyntax: "v3", - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-bb7b03c9610e982fd627-wrr", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-bb7b03c9610e982fd627-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1497,7 +1497,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb": { EntryPoints: []string{"web"}, - Service: "default-http-app-default-my-gateway-web-0-wrr", + Service: "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb-wrr", Rule: "Host(`foo.com`) && Path(`/foo`)", Priority: 100008, RuleSyntax: "v3", @@ -1505,7 +1505,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-default-my-gateway-web-0-wrr": { + "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1558,14 +1558,14 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb": { EntryPoints: []string{"web"}, - Service: "default-http-app-default-my-gateway-web-0-wrr", + Service: "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb-wrr", Rule: "Host(`foo.com`) && Path(`/foo`)", Priority: 100008, RuleSyntax: "v3", }, "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597": { EntryPoints: []string{"web"}, - Service: "bar-http-app-bar-my-gateway-web-0-wrr", + Service: "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597-wrr", Rule: "Host(`bar.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -1573,7 +1573,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-default-my-gateway-web-0-wrr": { + "default-http-app-default-my-gateway-web-0-efde1997778109a1f6eb-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1583,7 +1583,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, }, - "bar-http-app-bar-my-gateway-web-0-wrr": { + "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1652,7 +1652,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597": { EntryPoints: []string{"web"}, - Service: "bar-http-app-bar-my-gateway-web-0-wrr", + Service: "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597-wrr", Rule: "Host(`bar.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -1660,7 +1660,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "bar-http-app-bar-my-gateway-web-0-wrr": { + "bar-http-app-bar-my-gateway-web-0-66f5c78d03d948e36597-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1713,7 +1713,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr", Rule: "Host(`example.org`) && PathPrefix(`/`)", Priority: 13, RuleSyntax: "v3", @@ -1730,7 +1730,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1783,7 +1783,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr", Rule: "Host(`example.org`) && PathPrefix(`/`)", Priority: 13, RuleSyntax: "v3", @@ -1800,7 +1800,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -1853,7 +1853,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr", Rule: "Host(`example.org`) && PathPrefix(`/`)", Priority: 13, RuleSyntax: "v3", @@ -1870,7 +1870,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr": { Weighted: &dynamic.WeightedRoundRobin{}, }, }, @@ -1900,7 +1900,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr", Rule: "Host(`example.org`) && PathPrefix(`/`)", Priority: 13, RuleSyntax: "v3", @@ -1917,7 +1917,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-wrr": { Weighted: &dynamic.WeightedRoundRobin{}, }, }, @@ -1947,7 +1947,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr", Rule: "Host(`example.com`) && (Path(`/foo`) || PathPrefix(`/foo/`))", RuleSyntax: "v3", Priority: 10412, @@ -1962,7 +1962,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2015,7 +2015,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr", Rule: "Host(`example.com`) && (Path(`/foo`) || PathPrefix(`/foo/`))", RuleSyntax: "v3", Priority: 10412, @@ -2030,7 +2030,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2083,7 +2083,7 @@ func TestLoadHTTPRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr", Rule: "Host(`example.com`) && (Path(`/foo`) || PathPrefix(`/foo/`))", RuleSyntax: "v3", Priority: 10412, @@ -2100,7 +2100,7 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-7f90cf546b15efadf2f8-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2203,7 +2203,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2211,7 +2211,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2253,7 +2253,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2261,7 +2261,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2305,7 +2305,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2313,7 +2313,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2356,7 +2356,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2364,7 +2364,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2408,7 +2408,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2416,7 +2416,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2525,7 +2525,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2534,7 +2534,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2591,7 +2591,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr", Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", @@ -2602,7 +2602,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { "default-my-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, }, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-1c0cf64bde37d9d0df06-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -2931,7 +2931,7 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, { - desc: "Empty caused by multi ports service with wrong TargetPort", + desc: "Router with service in error caused by wrong TargetPort", entryPoints: map[string]Entrypoint{"TCP": { Address: ":8080", }}, @@ -2942,9 +2942,30 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, + Routers: map[string]*dynamic.TCPRouter{ + "default-TCP-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb": { + EntryPoints: []string{"TCP"}, + Rule: "HostSNI(`*`)", + RuleSyntax: "v3", + Service: "default-TCP-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-wrr", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-TCP-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-wrr": { + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{{ + Name: "default-TCP-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-err-lb", + Weight: ptr.To(1), + }}, + }, + }, + "default-TCP-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-err-lb": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{}, + }, + }, + }, ServersTransports: map[string]*dynamic.TCPServersTransport{}, }, HTTP: &dynamic.HTTPConfiguration{ @@ -2969,16 +2990,16 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-tcp-gateway-tcp": { + "default-tcp-app-1-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-1-my-tcp-gateway-tcp-wrr-0", + Service: "default-tcp-app-1-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-tcp-gateway-tcp-wrr-0": { + "default-tcp-app-1-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3027,22 +3048,22 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-tcp-gateway-tcp-1": { + "default-tcp-app-1-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp-1"}, - Service: "default-tcp-app-1-my-tcp-gateway-tcp-1-wrr-0", + Service: "default-tcp-app-1-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "default-tcp-app-2-my-tcp-gateway-tcp-2": { + "default-tcp-app-2-my-tcp-gateway-tcp-2-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp-2"}, - Service: "default-tcp-app-2-my-tcp-gateway-tcp-2-wrr-0", + Service: "default-tcp-app-2-my-tcp-gateway-tcp-2-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-tcp-gateway-tcp-1-wrr-0": { + "default-tcp-app-1-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3052,7 +3073,7 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, }, - "default-tcp-app-2-my-tcp-gateway-tcp-2-wrr-0": { + "default-tcp-app-2-my-tcp-gateway-tcp-2-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3113,30 +3134,22 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-my-tcp-gateway-tcp-1": { + "default-tcp-app-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp-1"}, - Service: "default-tcp-app-my-tcp-gateway-tcp-1-wrr", + Service: "default-tcp-app-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr", + Rule: "HostSNI(`*`)", + RuleSyntax: "v3", + }, + "default-tcp-app-my-tcp-gateway-tcp-1-1-e3b0c44298fc1c149afb": { + EntryPoints: []string{"tcp-1"}, + Service: "default-tcp-app-my-tcp-gateway-tcp-1-1-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-my-tcp-gateway-tcp-1-wrr": { - Weighted: &dynamic.TCPWeightedRoundRobin{ - Services: []dynamic.TCPWRRService{ - { - Name: "default-tcp-app-my-tcp-gateway-tcp-1-wrr-0", - Weight: ptr.To(1), - }, - { - Name: "default-tcp-app-my-tcp-gateway-tcp-1-wrr-1", - Weight: ptr.To(1), - }, - }, - }, - }, - "default-tcp-app-my-tcp-gateway-tcp-1-wrr-0": { + "default-tcp-app-my-tcp-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3146,7 +3159,7 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, }, - "default-tcp-app-my-tcp-gateway-tcp-1-wrr-1": { + "default-tcp-app-my-tcp-gateway-tcp-1-1-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3205,16 +3218,16 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-gateway-tcp": { + "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-1-my-gateway-tcp-wrr-0", + Service: "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-gateway-tcp-wrr-0": { + "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3265,9 +3278,9 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-gateway-tls": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tcp-app-1-my-gateway-tls-wrr-0", + Service: "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -3275,7 +3288,7 @@ func TestLoadTCPRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-gateway-tls-wrr-0": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{{ Name: "default-whoamitcp-9000", @@ -3329,16 +3342,16 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-default-my-tcp-gateway-tcp": { + "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-default-my-tcp-gateway-tcp-wrr-0", + Service: "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-default-my-tcp-gateway-tcp-wrr-0": { + "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3385,22 +3398,22 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-default-my-tcp-gateway-tcp": { + "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-default-my-tcp-gateway-tcp-wrr-0", + Service: "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "bar-tcp-app-bar-my-tcp-gateway-tcp": { + "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-wrr-0", + Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-default-my-tcp-gateway-tcp-wrr-0": { + "default-tcp-app-default-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3410,7 +3423,7 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, }, - "bar-tcp-app-bar-my-tcp-gateway-tcp-wrr-0": { + "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3469,16 +3482,16 @@ func TestLoadTCPRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "bar-tcp-app-bar-my-tcp-gateway-tcp": { + "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-wrr-0", + Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "bar-tcp-app-bar-my-tcp-gateway-tcp-wrr-0": { + "bar-tcp-app-bar-my-tcp-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3685,7 +3698,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, { - desc: "Empty caused by multi ports service with wrong TargetPort", + desc: "Router with service in error caused by wrong TargetPort", entryPoints: map[string]Entrypoint{"TCP": { Address: ":8080", }}, @@ -3696,9 +3709,31 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, + Routers: map[string]*dynamic.TCPRouter{ + "default-tls-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb": { + EntryPoints: []string{"TCP"}, + Rule: "HostSNI(`*`)", + RuleSyntax: "v3", + Service: "default-tls-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-wrr", + TLS: &dynamic.RouterTCPTLSConfig{}, + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-tls-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-wrr": { + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{{ + Name: "default-tls-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-err-lb", + Weight: ptr.To(1), + }}, + }, + }, + "default-tls-app-1-my-gateway-TCP-0-e3b0c44298fc1c149afb-err-lb": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{}, + }, + }, + }, ServersTransports: map[string]*dynamic.TCPServersTransport{}, }, HTTP: &dynamic.HTTPConfiguration{ @@ -3766,9 +3801,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-tls-gateway-tcp": { + "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-1-my-tls-gateway-tcp-wrr-0", + Service: "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -3776,7 +3811,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-tls-gateway-tcp-wrr-0": { + "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3832,9 +3867,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-tls-gateway-tcp": { + "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-1-my-tls-gateway-tcp-wrr-0", + Service: "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -3844,7 +3879,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-tls-gateway-tcp-wrr-0": { + "default-tcp-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3891,9 +3926,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270": { + "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270-wrr-0", + Service: "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -3903,7 +3938,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270-wrr-0": { + "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3951,16 +3986,16 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-tls-gateway-tls": { + "default-tcp-app-1-my-tls-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tcp-app-1-my-tls-gateway-tls-wrr-0", + Service: "default-tcp-app-1-my-tls-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, }, - "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a": { + "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a-wrr-0", + Service: "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -3970,7 +4005,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-tls-gateway-tls-wrr-0": { + "default-tcp-app-1-my-tls-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -3980,7 +4015,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, }, - "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a-wrr-0": { + "default-tls-app-1-my-tls-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4048,9 +4083,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-gateway-tls": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tcp-app-1-my-gateway-tls-wrr-0", + Service: "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -4058,7 +4093,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-gateway-tls-wrr-0": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4118,9 +4153,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0", + Service: "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4130,7 +4165,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4177,9 +4212,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0", + Service: "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4189,7 +4224,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4236,9 +4271,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0", + Service: "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4248,7 +4283,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4295,9 +4330,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-1-my-gateway-tls-d5342d75658583f03593": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-1-my-gateway-tls-d5342d75658583f03593-wrr-0", + Service: "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.example.com`) || HostSNI(`bar.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4307,7 +4342,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-1-my-gateway-tls-d5342d75658583f03593-wrr-0": { + "default-tls-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4354,9 +4389,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5": { + "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0", + Service: "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.default`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4366,7 +4401,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0": { + "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4413,18 +4448,18 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5": { + "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0", + Service: "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.default`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ Passthrough: true, }, }, - "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26": { + "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0", + Service: "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.bar`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4434,7 +4469,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0": { + "default-tls-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4444,7 +4479,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, }, - "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0": { + "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4503,9 +4538,9 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26": { + "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0", + Service: "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`foo.bar`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4515,7 +4550,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0": { + "bar-tls-app-bar-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4562,9 +4597,18 @@ func TestLoadTLSRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a": { + "default-tls-app-my-gateway-tcp-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp-1"}, - Service: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr", + Service: "default-tls-app-my-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr", + Rule: "HostSNI(`*`)", + RuleSyntax: "v3", + TLS: &dynamic.RouterTCPTLSConfig{ + Passthrough: true, + }, + }, + "default-tls-app-my-gateway-tcp-1-1-e3b0c44298fc1c149afb": { + EntryPoints: []string{"tcp-1"}, + Service: "default-tls-app-my-gateway-tcp-1-1-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4574,21 +4618,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr": { - Weighted: &dynamic.TCPWeightedRoundRobin{ - Services: []dynamic.TCPWRRService{ - { - Name: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr-0", - Weight: ptr.To(1), - }, - { - Name: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr-1", - Weight: ptr.To(1), - }, - }, - }, - }, - "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr-0": { + "default-tls-app-my-gateway-tcp-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4598,7 +4628,7 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, }, - "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr-1": { + "default-tls-app-my-gateway-tcp-1-1-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4809,22 +4839,22 @@ func TestLoadMixedRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-gateway-tcp": { + "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-1-my-gateway-tcp-wrr-0", + Service: "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "default-tcp-app-1-my-gateway-tls-1": { + "default-tcp-app-1-my-gateway-tls-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-1"}, - Service: "default-tcp-app-1-my-gateway-tls-1-wrr-0", + Service: "default-tcp-app-1-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, }, - "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1": { + "default-tls-app-1-my-gateway-tls-2-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-2"}, - Service: "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0", + Service: "default-tls-app-1-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`pass.tls.foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -4834,7 +4864,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-gateway-tcp-wrr-0": { + "default-tcp-app-1-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4844,7 +4874,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tcp-app-1-my-gateway-tls-1-wrr-0": { + "default-tcp-app-1-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4854,7 +4884,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0": { + "default-tls-app-1-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -4883,14 +4913,14 @@ func TestLoadMixedRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-1-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "default-http-app-1-my-gateway-web-0-wrr", + Service: "default-http-app-1-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "default-http-app-1-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-1-my-gateway-websecure-0-wrr", + Service: "default-http-app-1-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -4899,7 +4929,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-web-0-wrr": { + "default-http-app-1-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -4909,7 +4939,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-http-app-1-my-gateway-websecure-0-wrr": { + "default-http-app-1-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -4996,22 +5026,22 @@ func TestLoadMixedRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-default-my-gateway-tcp": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-default-my-gateway-tcp-wrr-0", + Service: "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "default-tcp-app-default-my-gateway-tls-1": { + "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-1"}, - Service: "default-tcp-app-default-my-gateway-tls-1-wrr-0", + Service: "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, }, - "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": { + "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-2"}, - Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0", + Service: "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`pass.tls.foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -5021,7 +5051,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-default-my-gateway-tcp-wrr-0": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5031,7 +5061,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tcp-app-default-my-gateway-tls-1-wrr-0": { + "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5041,7 +5071,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0": { + "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5070,14 +5100,14 @@ func TestLoadMixedRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "default-http-app-default-my-gateway-web-0-wrr", + Service: "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-default-my-gateway-websecure-0-wrr", + Service: "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -5086,7 +5116,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-default-my-gateway-web-0-wrr": { + "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5096,7 +5126,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-http-app-default-my-gateway-websecure-0-wrr": { + "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5155,37 +5185,37 @@ func TestLoadMixedRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-default-my-gateway-tcp": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-default-my-gateway-tcp-wrr-0", + Service: "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "default-tcp-app-default-my-gateway-tls-1": { + "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-1"}, - Service: "default-tcp-app-default-my-gateway-tls-1-wrr-0", + Service: "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, }, - "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": { + "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-2"}, - Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0", + Service: "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`pass.tls.foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ Passthrough: true, }, }, - "bar-tcp-app-bar-my-gateway-tcp": { + "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "bar-tcp-app-bar-my-gateway-tcp-wrr-0", + Service: "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "bar-tcp-app-bar-my-gateway-tls-1": { + "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-1"}, - Service: "bar-tcp-app-bar-my-gateway-tls-1-wrr-0", + Service: "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -5193,7 +5223,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-default-my-gateway-tcp-wrr-0": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5203,7 +5233,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tcp-app-default-my-gateway-tls-1-wrr-0": { + "default-tcp-app-default-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5213,7 +5243,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0": { + "default-tls-app-default-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5247,7 +5277,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-tcp-app-bar-my-gateway-tcp-wrr-0": { + "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5257,7 +5287,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-tcp-app-bar-my-gateway-tls-1-wrr-0": { + "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5274,14 +5304,14 @@ func TestLoadMixedRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "default-http-app-default-my-gateway-web-0-wrr", + Service: "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-default-my-gateway-websecure-0-wrr", + Service: "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -5289,14 +5319,14 @@ func TestLoadMixedRoutes(t *testing.T) { }, "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "bar-http-app-bar-my-gateway-web-0-wrr", + Service: "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "bar-http-app-bar-my-gateway-websecure-0-wrr", + Service: "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -5305,7 +5335,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-default-my-gateway-web-0-wrr": { + "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5315,7 +5345,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-http-app-default-my-gateway-websecure-0-wrr": { + "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5357,7 +5387,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-http-app-bar-my-gateway-web-0-wrr": { + "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5367,7 +5397,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-http-app-bar-my-gateway-websecure-0-wrr": { + "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5410,22 +5440,22 @@ func TestLoadMixedRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "bar-tcp-app-bar-my-gateway-tcp": { + "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "bar-tcp-app-bar-my-gateway-tcp-wrr-0", + Service: "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "bar-tcp-app-bar-my-gateway-tls-1": { + "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-1"}, - Service: "bar-tcp-app-bar-my-gateway-tls-1-wrr-0", + Service: "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, }, - "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1": { + "bar-tls-app-bar-my-gateway-tls-2-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls-2"}, - Service: "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0", + Service: "bar-tls-app-bar-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`pass.tls.foo.example.com`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{ @@ -5447,7 +5477,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-tcp-app-bar-my-gateway-tcp-wrr-0": { + "bar-tcp-app-bar-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5457,7 +5487,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-tcp-app-bar-my-gateway-tls-1-wrr-0": { + "bar-tcp-app-bar-my-gateway-tls-1-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5467,7 +5497,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0": { + "bar-tls-app-bar-my-gateway-tls-2-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5484,14 +5514,14 @@ func TestLoadMixedRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "bar-http-app-bar-my-gateway-web-0-wrr", + Service: "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "bar-http-app-bar-my-gateway-websecure-0-wrr", + Service: "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -5516,7 +5546,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-http-app-bar-my-gateway-web-0-wrr": { + "bar-http-app-bar-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5526,7 +5556,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "bar-http-app-bar-my-gateway-websecure-0-wrr": { + "bar-http-app-bar-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5568,15 +5598,15 @@ func TestLoadMixedRoutes(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-default-my-gateway-tcp": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tcp"}, - Service: "default-tcp-app-default-my-gateway-tcp-wrr-0", + Service: "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", }, - "default-tcp-app-default-my-gateway-tls": { + "default-tcp-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tcp-app-default-my-gateway-tls-wrr-0", + Service: "default-tcp-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -5584,7 +5614,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-default-my-gateway-tcp-wrr-0": { + "default-tcp-app-default-my-gateway-tcp-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5594,7 +5624,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-tcp-app-default-my-gateway-tls-wrr-0": { + "default-tcp-app-default-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{ { @@ -5623,14 +5653,14 @@ func TestLoadMixedRoutes(t *testing.T) { Routers: map[string]*dynamic.Router{ "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd": { EntryPoints: []string{"web"}, - Service: "default-http-app-default-my-gateway-web-0-wrr", + Service: "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", }, "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd": { EntryPoints: []string{"websecure"}, - Service: "default-http-app-default-my-gateway-websecure-0-wrr", + Service: "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr", Rule: "PathPrefix(`/`)", Priority: 2, RuleSyntax: "v3", @@ -5639,7 +5669,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-default-my-gateway-web-0-wrr": { + "default-http-app-default-my-gateway-web-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5649,7 +5679,7 @@ func TestLoadMixedRoutes(t *testing.T) { }, }, }, - "default-http-app-default-my-gateway-websecure-0-wrr": { + "default-http-app-default-my-gateway-websecure-0-a431b128267aabc954fd-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { @@ -5851,9 +5881,9 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ - "default-tcp-app-1-my-gateway-tls": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb": { EntryPoints: []string{"tls"}, - Service: "default-tcp-app-1-my-gateway-tls-wrr-0", + Service: "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr", Rule: "HostSNI(`*`)", RuleSyntax: "v3", TLS: &dynamic.RouterTCPTLSConfig{}, @@ -5861,7 +5891,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { }, Middlewares: map[string]*dynamic.TCPMiddleware{}, Services: map[string]*dynamic.TCPService{ - "default-tcp-app-1-my-gateway-tls-wrr-0": { + "default-tcp-app-1-my-gateway-tls-0-e3b0c44298fc1c149afb-wrr": { Weighted: &dynamic.TCPWeightedRoundRobin{ Services: []dynamic.TCPWRRService{{ Name: "default-whoamitcp-9000", @@ -6002,14 +6032,14 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { "default-http-app-1-my-gateway-http-0-d40286ed9f4652ca2108": { EntryPoints: []string{"http"}, Rule: "Host(`foo.example.com`) && PathPrefix(`/`)", - Service: "default-http-app-1-my-gateway-http-0-wrr", + Service: "default-http-app-1-my-gateway-http-0-d40286ed9f4652ca2108-wrr", RuleSyntax: "v3", Priority: 17, }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-http-app-1-my-gateway-http-0-wrr": { + "default-http-app-1-my-gateway-http-0-d40286ed9f4652ca2108-wrr": { Weighted: &dynamic.WeightedRoundRobin{ Services: []dynamic.WRRService{ { diff --git a/pkg/provider/kubernetes/gateway/tcproute.go b/pkg/provider/kubernetes/gateway/tcproute.go index 23e7bcd80..63b2e214c 100644 --- a/pkg/provider/kubernetes/gateway/tcproute.go +++ b/pkg/provider/kubernetes/gateway/tcproute.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "strconv" + "strings" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" @@ -108,182 +109,241 @@ func (p *Provider) loadTCPRoute(listener gatewayListener, route *gatev1alpha2.TC Reason: string(gatev1.RouteConditionResolvedRefs), } - router := dynamic.TCPRouter{ - Rule: "HostSNI(`*`)", - EntryPoints: []string{listener.EPName}, - RuleSyntax: "v3", - } - - if listener.Protocol == gatev1.TLSProtocolType && listener.TLS != nil { - // TODO support let's encrypt - router.TLS = &dynamic.RouterTCPTLSConfig{ - Passthrough: listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough, - } - } - - // Adding the gateway desc and the entryPoint desc prevents overlapping of routers build from the same routes. - routerName := provider.Normalize(route.Namespace + "-" + route.Name + "-" + listener.GWName + "-" + listener.EPName) - - var ruleServiceNames []string - for i, rule := range route.Spec.Rules { + for ri, rule := range route.Spec.Rules { if rule.BackendRefs == nil { // Should not happen due to validation. // https://github.com/kubernetes-sigs/gateway-api/blob/v0.4.0/apis/v1alpha2/tcproute_types.go#L76 continue } - wrrService, subServices, err := p.loadTCPServices(route.Namespace, rule.BackendRefs) - if err != nil { - return conf, metav1.Condition{ - Type: string(gatev1.RouteConditionResolvedRefs), - Status: metav1.ConditionFalse, - ObservedGeneration: route.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(gatev1.RouteReasonBackendNotFound), - Message: fmt.Sprintf("Cannot load TCPRoute service %s/%s: %v", route.Namespace, route.Name, err), + router := dynamic.TCPRouter{ + Rule: "HostSNI(`*`)", + EntryPoints: []string{listener.EPName}, + RuleSyntax: "v3", + } + + if listener.Protocol == gatev1.TLSProtocolType && listener.TLS != nil { + router.TLS = &dynamic.RouterTCPTLSConfig{ + Passthrough: listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough, } } - for svcName, svc := range subServices { - conf.TCP.Services[svcName] = svc + // Adding the gateway desc and the entryPoint desc prevents overlapping of routers build from the same routes. + routeKey := provider.Normalize(fmt.Sprintf("%s-%s-%s-%s-%d", route.Namespace, route.Name, listener.GWName, listener.EPName, ri)) + // Routing criteria should be introduced at some point. + routerName := makeRouterName("", routeKey) + + if len(rule.BackendRefs) == 1 && isInternalService(rule.BackendRefs[0]) { + router.Service = string(rule.BackendRefs[0].Name) + conf.TCP.Routers[routerName] = &router + continue } - serviceName := fmt.Sprintf("%s-wrr-%d", routerName, i) - conf.TCP.Services[serviceName] = wrrService + var serviceCondition *metav1.Condition + router.Service, serviceCondition = p.loadTCPWRRService(conf, routerName, rule.BackendRefs, route) + if serviceCondition != nil { + condition = *serviceCondition + } - ruleServiceNames = append(ruleServiceNames, serviceName) - } - - if len(ruleServiceNames) == 1 { - router.Service = ruleServiceNames[0] conf.TCP.Routers[routerName] = &router - return conf, condition } - routeServiceKey := routerName + "-wrr" - routeService := &dynamic.TCPService{Weighted: &dynamic.TCPWeightedRoundRobin{}} - - for _, name := range ruleServiceNames { - service := dynamic.TCPWRRService{Name: name} - service.SetDefaults() - - routeService.Weighted.Services = append(routeService.Weighted.Services, service) - } - - conf.TCP.Services[routeServiceKey] = routeService - - router.Service = routeServiceKey - conf.TCP.Routers[routerName] = &router - return conf, condition } -// loadTCPServices is generating a WRR service, even when there is only one target. -func (p *Provider) loadTCPServices(namespace string, backendRefs []gatev1.BackendRef) (*dynamic.TCPService, map[string]*dynamic.TCPService, error) { - services := map[string]*dynamic.TCPService{} - - wrrSvc := &dynamic.TCPService{ - Weighted: &dynamic.TCPWeightedRoundRobin{ - Services: []dynamic.TCPWRRService{}, - }, +// loadTCPWRRService is generating a WRR service, even when there is only one target. +func (p *Provider) loadTCPWRRService(conf *dynamic.Configuration, routeKey string, backendRefs []gatev1.BackendRef, route *gatev1alpha2.TCPRoute) (string, *metav1.Condition) { + name := routeKey + "-wrr" + if _, ok := conf.TCP.Services[name]; ok { + return name, nil } + var wrr dynamic.TCPWeightedRoundRobin + var condition *metav1.Condition for _, backendRef := range backendRefs { - if backendRef.Group == nil || backendRef.Kind == nil { - // Should not happen as this is validated by kubernetes + svcName, svc, errCondition := p.loadTCPService(route, backendRef) + weight := ptr.To(int(ptr.Deref(backendRef.Weight, 1))) + + if errCondition != nil { + condition = errCondition + + errName := routeKey + "-err-lb" + conf.TCP.Services[errName] = &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{}, + }, + } + + wrr.Services = append(wrr.Services, dynamic.TCPWRRService{ + Name: errName, + Weight: weight, + }) continue } - if isInternalService(backendRef) { - return nil, nil, fmt.Errorf("traefik internal service %s is not allowed in a WRR loadbalancer", backendRef.Name) + if svc != nil { + conf.TCP.Services[svcName] = svc } - weight := int(ptr.Deref(backendRef.Weight, 1)) + wrr.Services = append(wrr.Services, dynamic.TCPWRRService{ + Name: svcName, + Weight: weight, + }) + } - if isTraefikService(backendRef) { - wrrSvc.Weighted.Services = append(wrrSvc.Weighted.Services, dynamic.TCPWRRService{Name: string(backendRef.Name), Weight: &weight}) - continue + conf.TCP.Services[name] = &dynamic.TCPService{Weighted: &wrr} + return name, condition +} + +func (p *Provider) loadTCPService(route *gatev1alpha2.TCPRoute, backendRef gatev1.BackendRef) (string, *dynamic.TCPService, *metav1.Condition) { + kind := ptr.Deref(backendRef.Kind, kindService) + + group := groupCore + if backendRef.Group != nil && *backendRef.Group != "" { + group = string(*backendRef.Group) + } + + namespace := route.Namespace + if backendRef.Namespace != nil && *backendRef.Namespace != "" { + namespace = string(*backendRef.Namespace) + } + + serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name)) + + if err := p.isReferenceGranted(kindTCPRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonRefNotPermitted), + Message: fmt.Sprintf("Cannot load TCPRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), } + } - if *backendRef.Group != "" && *backendRef.Group != groupCore && *backendRef.Kind != "Service" { - return nil, nil, fmt.Errorf("unsupported BackendRef %s/%s/%s", *backendRef.Group, *backendRef.Kind, backendRef.Name) - } - - if backendRef.Port == nil { - return nil, nil, errors.New("port is required for Kubernetes Service reference") - } - - service, exists, err := p.client.GetService(namespace, string(backendRef.Name)) + if group != groupCore || kind != kindService { + name, err := p.loadTCPBackendRef(backendRef) if err != nil { - return nil, nil, fmt.Errorf("getting service: %w", err) - } - if !exists { - return nil, nil, errors.New("service not found") + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonInvalidKind), + Message: fmt.Sprintf("Cannot load TCPRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), + } } - var svcPort *corev1.ServicePort - for _, p := range service.Spec.Ports { - if p.Port == int32(*backendRef.Port) { - svcPort = &p + return name, nil, nil + } + + port := ptr.Deref(backendRef.Port, gatev1.PortNumber(0)) + if port == 0 { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonUnsupportedProtocol), + Message: fmt.Sprintf("Cannot load TCPRoute BackendRef %s/%s/%s/%s port is required", group, kind, namespace, backendRef.Name), + } + } + + portStr := strconv.FormatInt(int64(port), 10) + serviceName = provider.Normalize(serviceName + "-" + portStr) + + lb, err := p.loadTCPServers(namespace, backendRef) + if err != nil { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonBackendNotFound), + Message: fmt.Sprintf("Cannot load TCPRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), + } + } + + return serviceName, &dynamic.TCPService{LoadBalancer: lb}, nil +} + +func (p *Provider) loadTCPServers(namespace string, backendRef gatev1.BackendRef) (*dynamic.TCPServersLoadBalancer, error) { + if backendRef.Port == nil { + return nil, errors.New("port is required for Kubernetes Service reference") + } + + service, exists, err := p.client.GetService(namespace, string(backendRef.Name)) + if err != nil { + return nil, fmt.Errorf("getting service: %w", err) + } + if !exists { + return nil, errors.New("service not found") + } + + var svcPort *corev1.ServicePort + for _, p := range service.Spec.Ports { + if p.Port == int32(*backendRef.Port) { + svcPort = &p + break + } + } + if svcPort == nil { + return nil, fmt.Errorf("service port %d not found", *backendRef.Port) + } + + endpointSlices, err := p.client.ListEndpointSlicesForService(namespace, string(backendRef.Name)) + if err != nil { + return nil, fmt.Errorf("getting endpointslices: %w", err) + } + if len(endpointSlices) == 0 { + return nil, errors.New("endpointslices not found") + } + + lb := &dynamic.TCPServersLoadBalancer{} + + addresses := map[string]struct{}{} + for _, endpointSlice := range endpointSlices { + var port int32 + for _, p := range endpointSlice.Ports { + if svcPort.Name == *p.Name { + port = *p.Port break } } - if svcPort == nil { - return nil, nil, fmt.Errorf("service port %d not found", *backendRef.Port) + if port == 0 { + continue } - endpointSlices, err := p.client.ListEndpointSlicesForService(namespace, string(backendRef.Name)) - if err != nil { - return nil, nil, fmt.Errorf("getting endpointslices: %w", err) - } - if len(endpointSlices) == 0 { - return nil, nil, errors.New("endpointslices not found") - } - - svc := dynamic.TCPService{LoadBalancer: &dynamic.TCPServersLoadBalancer{}} - - addresses := map[string]struct{}{} - for _, endpointSlice := range endpointSlices { - var port int32 - for _, p := range endpointSlice.Ports { - if svcPort.Name == *p.Name { - port = *p.Port - break - } - } - if port == 0 { + for _, endpoint := range endpointSlice.Endpoints { + if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready { continue } - for _, endpoint := range endpointSlice.Endpoints { - if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready { + for _, address := range endpoint.Addresses { + if _, ok := addresses[address]; ok { continue } - for _, address := range endpoint.Addresses { - if _, ok := addresses[address]; ok { - continue - } - - addresses[address] = struct{}{} - svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, dynamic.TCPServer{ - Address: net.JoinHostPort(address, strconv.Itoa(int(port))), - }) - } + addresses[address] = struct{}{} + lb.Servers = append(lb.Servers, dynamic.TCPServer{ + // TODO determine whether the servers needs TLS, from the port? + Address: net.JoinHostPort(address, strconv.Itoa(int(port))), + }) } } - - serviceName := provider.Normalize(service.Namespace + "-" + service.Name + "-" + strconv.Itoa(int(svcPort.Port))) - services[serviceName] = &svc - - wrrSvc.Weighted.Services = append(wrrSvc.Weighted.Services, dynamic.TCPWRRService{Name: serviceName, Weight: &weight}) } - if len(wrrSvc.Weighted.Services) == 0 { - return nil, nil, errors.New("no service has been created") + return lb, nil +} + +func (p *Provider) loadTCPBackendRef(backendRef gatev1.BackendRef) (string, error) { + // Support for cross-provider references (e.g: api@internal). + // This provides the same behavior as for IngressRoutes. + if *backendRef.Kind == "TraefikService" && strings.Contains(string(backendRef.Name), "@") { + return string(backendRef.Name), nil } - return wrrSvc, services, nil + return "", fmt.Errorf("unsupported BackendRef %s/%s/%s", *backendRef.Group, *backendRef.Kind, backendRef.Name) } func mergeTCPConfiguration(from, to *dynamic.Configuration) { diff --git a/pkg/provider/kubernetes/gateway/tlsroute.go b/pkg/provider/kubernetes/gateway/tlsroute.go index b8a853a44..adbff1da5 100644 --- a/pkg/provider/kubernetes/gateway/tlsroute.go +++ b/pkg/provider/kubernetes/gateway/tlsroute.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "strconv" "strings" "github.com/rs/zerolog/log" @@ -11,6 +12,7 @@ import ( "github.com/traefik/traefik/v3/pkg/provider" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ktypes "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" gatev1 "sigs.k8s.io/gateway-api/apis/v1" gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -109,74 +111,159 @@ func (p *Provider) loadTLSRoute(listener gatewayListener, route *gatev1alpha2.TL Reason: string(gatev1.RouteConditionResolvedRefs), } - router := dynamic.TCPRouter{ - RuleSyntax: "v3", - Rule: hostSNIRule(hostnames), - EntryPoints: []string{listener.EPName}, - TLS: &dynamic.RouterTCPTLSConfig{ - Passthrough: listener.TLS != nil && listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough, - }, - } - - // Adding the gateway desc and the entryPoint desc prevents overlapping of routers build from the same routes. - routeKey := provider.Normalize(route.Namespace + "-" + route.Name + "-" + listener.GWName + "-" + listener.EPName) - routerName := makeRouterName(router.Rule, routeKey) - - var ruleServiceNames []string - for i, routeRule := range route.Spec.Rules { + for ri, routeRule := range route.Spec.Rules { if len(routeRule.BackendRefs) == 0 { // Should not happen due to validation. // https://github.com/kubernetes-sigs/gateway-api/blob/v0.4.0/apis/v1alpha2/tlsroute_types.go#L120 continue } - wrrService, subServices, err := p.loadTCPServices(route.Namespace, routeRule.BackendRefs) + router := dynamic.TCPRouter{ + RuleSyntax: "v3", + Rule: hostSNIRule(hostnames), + EntryPoints: []string{listener.EPName}, + TLS: &dynamic.RouterTCPTLSConfig{ + Passthrough: listener.TLS != nil && listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough, + }, + } + + // Adding the gateway desc and the entryPoint desc prevents overlapping of routers build from the same routes. + routeKey := provider.Normalize(fmt.Sprintf("%s-%s-%s-%s-%d", route.Namespace, route.Name, listener.GWName, listener.EPName, ri)) + // Routing criteria should be introduced at some point. + routerName := makeRouterName("", routeKey) + + if len(routeRule.BackendRefs) == 1 && isInternalService(routeRule.BackendRefs[0]) { + router.Service = string(routeRule.BackendRefs[0].Name) + conf.TCP.Routers[routerName] = &router + continue + } + + var serviceCondition *metav1.Condition + router.Service, serviceCondition = p.loadTLSWRRService(conf, routerName, routeRule.BackendRefs, route) + if serviceCondition != nil { + condition = *serviceCondition + } + + conf.TCP.Routers[routerName] = &router + } + + return conf, condition +} + +// loadTLSWRRService is generating a WRR service, even when there is only one target. +func (p *Provider) loadTLSWRRService(conf *dynamic.Configuration, routeKey string, backendRefs []gatev1.BackendRef, route *gatev1alpha2.TLSRoute) (string, *metav1.Condition) { + name := routeKey + "-wrr" + if _, ok := conf.TCP.Services[name]; ok { + return name, nil + } + + var wrr dynamic.TCPWeightedRoundRobin + var condition *metav1.Condition + for _, backendRef := range backendRefs { + svcName, svc, errCondition := p.loadTLSService(route, backendRef) + weight := ptr.To(int(ptr.Deref(backendRef.Weight, 1))) + + if errCondition != nil { + condition = errCondition + + errName := routeKey + "-err-lb" + conf.TCP.Services[errName] = &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{}, + }, + } + + wrr.Services = append(wrr.Services, dynamic.TCPWRRService{ + Name: errName, + Weight: weight, + }) + continue + } + + if svc != nil { + conf.TCP.Services[svcName] = svc + } + + wrr.Services = append(wrr.Services, dynamic.TCPWRRService{ + Name: svcName, + Weight: weight, + }) + } + + conf.TCP.Services[name] = &dynamic.TCPService{Weighted: &wrr} + return name, condition +} + +func (p *Provider) loadTLSService(route *gatev1alpha2.TLSRoute, backendRef gatev1.BackendRef) (string, *dynamic.TCPService, *metav1.Condition) { + kind := ptr.Deref(backendRef.Kind, kindService) + + group := groupCore + if backendRef.Group != nil && *backendRef.Group != "" { + group = string(*backendRef.Group) + } + + namespace := route.Namespace + if backendRef.Namespace != nil && *backendRef.Namespace != "" { + namespace = string(*backendRef.Namespace) + } + + serviceName := provider.Normalize(namespace + "-" + string(backendRef.Name)) + + if err := p.isReferenceGranted(kindTLSRoute, route.Namespace, group, string(kind), string(backendRef.Name), namespace); err != nil { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonRefNotPermitted), + Message: fmt.Sprintf("Cannot load TLSRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), + } + } + + if group != groupCore || kind != kindService { + name, err := p.loadTCPBackendRef(backendRef) if err != nil { - // update "ResolvedRefs" status true with "InvalidBackendRefs" reason - condition = metav1.Condition{ + return serviceName, nil, &metav1.Condition{ Type: string(gatev1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, ObservedGeneration: route.Generation, LastTransitionTime: metav1.Now(), - Reason: string(gatev1.RouteReasonBackendNotFound), - Message: fmt.Sprintf("Cannot load TLSRoute service %s/%s: %v", route.Namespace, route.Name, err), + Reason: string(gatev1.RouteReasonInvalidKind), + Message: fmt.Sprintf("Cannot load TLSRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), } - continue } - for svcName, svc := range subServices { - conf.TCP.Services[svcName] = svc + return name, nil, nil + } + + port := ptr.Deref(backendRef.Port, gatev1.PortNumber(0)) + if port == 0 { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonUnsupportedProtocol), + Message: fmt.Sprintf("Cannot load TLSRoute BackendRef %s/%s/%s/%s port is required", group, kind, namespace, backendRef.Name), } - - serviceName := fmt.Sprintf("%s-wrr-%d", routerName, i) - conf.TCP.Services[serviceName] = wrrService - - ruleServiceNames = append(ruleServiceNames, serviceName) } - if len(ruleServiceNames) == 1 { - router.Service = ruleServiceNames[0] - conf.TCP.Routers[routerName] = &router + portStr := strconv.FormatInt(int64(port), 10) + serviceName = provider.Normalize(serviceName + "-" + portStr) - return conf, condition + lb, err := p.loadTCPServers(namespace, backendRef) + if err != nil { + return serviceName, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonBackendNotFound), + Message: fmt.Sprintf("Cannot load TLSRoute BackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err), + } } - routeServiceKey := routerName + "-wrr" - routeService := &dynamic.TCPService{Weighted: &dynamic.TCPWeightedRoundRobin{}} - - for _, name := range ruleServiceNames { - service := dynamic.TCPWRRService{Name: name} - service.SetDefaults() - - routeService.Weighted.Services = append(routeService.Weighted.Services, service) - } - - conf.TCP.Services[routeServiceKey] = routeService - - router.Service = routeServiceKey - conf.TCP.Routers[routerName] = &router - - return conf, condition + return serviceName, &dynamic.TCPService{LoadBalancer: lb}, nil } func hostSNIRule(hostnames []gatev1.Hostname) string { diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go index 4fcc98f08..047661735 100644 --- a/pkg/provider/kv/kv_test.go +++ b/pkg/provider/kv/kv_test.go @@ -61,6 +61,7 @@ func Test_buildConfiguration(t *testing.T) { "traefik/http/services/Service01/loadBalancer/servers/0/url": "foobar", "traefik/http/services/Service01/loadBalancer/servers/1/url": "foobar", "traefik/http/services/Service02/mirroring/service": "foobar", + "traefik/http/services/Service02/mirroring/mirrorBody": "true", "traefik/http/services/Service02/mirroring/maxBodySize": "42", "traefik/http/services/Service02/mirroring/mirrors/0/name": "foobar", "traefik/http/services/Service02/mirroring/mirrors/0/percent": "42", @@ -676,6 +677,7 @@ func Test_buildConfiguration(t *testing.T) { "Service02": { Mirroring: &dynamic.Mirroring{ Service: "foobar", + MirrorBody: func(v bool) *bool { return &v }(true), MaxBodySize: func(v int64) *int64 { return &v }(42), Mirrors: []dynamic.MirrorService{ { diff --git a/pkg/server/service/loadbalancer/mirror/mirror.go b/pkg/server/service/loadbalancer/mirror/mirror.go index 201dade9a..91cf364a3 100644 --- a/pkg/server/service/loadbalancer/mirror/mirror.go +++ b/pkg/server/service/loadbalancer/mirror/mirror.go @@ -25,6 +25,7 @@ type Mirroring struct { rw http.ResponseWriter routinePool *safe.Pool + mirrorBody bool maxBodySize int64 wantsHealthCheck bool @@ -33,11 +34,12 @@ type Mirroring struct { } // New returns a new instance of *Mirroring. -func New(handler http.Handler, pool *safe.Pool, maxBodySize int64, hc *dynamic.HealthCheck) *Mirroring { +func New(handler http.Handler, pool *safe.Pool, mirrorBody bool, maxBodySize int64, hc *dynamic.HealthCheck) *Mirroring { return &Mirroring{ routinePool: pool, handler: handler, rw: blackHoleResponseWriter{}, + mirrorBody: mirrorBody, maxBodySize: maxBodySize, wantsHealthCheck: hc != nil, } @@ -83,7 +85,7 @@ func (m *Mirroring) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } logger := log.Ctx(req.Context()) - rr, bytesRead, err := newReusableRequest(req, m.maxBodySize) + rr, bytesRead, err := newReusableRequest(req, m.mirrorBody, m.maxBodySize) if err != nil && !errors.Is(err, errBodyTooLarge) { http.Error(rw, fmt.Sprintf("%s: creating reusable request: %v", http.StatusText(http.StatusInternalServerError), err), http.StatusInternalServerError) @@ -200,11 +202,11 @@ var errBodyTooLarge = errors.New("request body too large") // if the returned error is errBodyTooLarge, newReusableRequest also returns the // bytes that were already consumed from the request's body. -func newReusableRequest(req *http.Request, maxBodySize int64) (*reusableRequest, []byte, error) { +func newReusableRequest(req *http.Request, mirrorBody bool, maxBodySize int64) (*reusableRequest, []byte, error) { if req == nil { return nil, nil, errors.New("nil input request") } - if req.Body == nil || req.ContentLength == 0 { + if req.Body == nil || req.ContentLength == 0 || !mirrorBody { return &reusableRequest{req: req}, nil, nil } diff --git a/pkg/server/service/loadbalancer/mirror/mirror_test.go b/pkg/server/service/loadbalancer/mirror/mirror_test.go index c48637006..94a4c62a0 100644 --- a/pkg/server/service/loadbalancer/mirror/mirror_test.go +++ b/pkg/server/service/loadbalancer/mirror/mirror_test.go @@ -21,7 +21,7 @@ func TestMirroringOn100(t *testing.T) { rw.WriteHeader(http.StatusOK) }) pool := safe.NewPool(context.Background()) - mirror := New(handler, pool, defaultMaxBodySize, nil) + mirror := New(handler, pool, true, defaultMaxBodySize, nil) err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { atomic.AddInt32(&countMirror1, 1) }), 10) @@ -50,7 +50,7 @@ func TestMirroringOn10(t *testing.T) { rw.WriteHeader(http.StatusOK) }) pool := safe.NewPool(context.Background()) - mirror := New(handler, pool, defaultMaxBodySize, nil) + mirror := New(handler, pool, true, defaultMaxBodySize, nil) err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { atomic.AddInt32(&countMirror1, 1) }), 10) @@ -74,7 +74,7 @@ func TestMirroringOn10(t *testing.T) { } func TestInvalidPercent(t *testing.T) { - mirror := New(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), safe.NewPool(context.Background()), defaultMaxBodySize, nil) + mirror := New(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), safe.NewPool(context.Background()), true, defaultMaxBodySize, nil) err := mirror.AddMirror(nil, -1) assert.Error(t, err) @@ -93,7 +93,7 @@ func TestHijack(t *testing.T) { rw.WriteHeader(http.StatusOK) }) pool := safe.NewPool(context.Background()) - mirror := New(handler, pool, defaultMaxBodySize, nil) + mirror := New(handler, pool, true, defaultMaxBodySize, nil) var mirrorRequest bool err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -117,7 +117,7 @@ func TestFlush(t *testing.T) { rw.WriteHeader(http.StatusOK) }) pool := safe.NewPool(context.Background()) - mirror := New(handler, pool, defaultMaxBodySize, nil) + mirror := New(handler, pool, true, defaultMaxBodySize, nil) var mirrorRequest bool err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -154,7 +154,7 @@ func TestMirroringWithBody(t *testing.T) { rw.WriteHeader(http.StatusOK) }) - mirror := New(handler, pool, defaultMaxBodySize, nil) + mirror := New(handler, pool, true, defaultMaxBodySize, nil) for range numMirrors { err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { @@ -177,13 +177,55 @@ func TestMirroringWithBody(t *testing.T) { assert.Equal(t, numMirrors, int(val)) } +func TestMirroringWithIgnoredBody(t *testing.T) { + const numMirrors = 10 + + var ( + countMirror int32 + body = []byte(`body`) + emptyBody = []byte(``) + ) + + pool := safe.NewPool(context.Background()) + + handler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + assert.NotNil(t, r.Body) + bb, err := io.ReadAll(r.Body) + assert.NoError(t, err) + assert.Equal(t, body, bb) + rw.WriteHeader(http.StatusOK) + }) + + mirror := New(handler, pool, false, defaultMaxBodySize, nil) + + 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) + assert.NoError(t, err) + assert.Equal(t, emptyBody, bb) + atomic.AddInt32(&countMirror, 1) + }), 100) + assert.NoError(t, err) + } + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body)) + + mirror.ServeHTTP(httptest.NewRecorder(), req) + + pool.Stop() + + val := atomic.LoadInt32(&countMirror) + assert.Equal(t, numMirrors, int(val)) +} + func TestCloneRequest(t *testing.T) { t.Run("http request body is nil", func(t *testing.T) { req, err := http.NewRequest(http.MethodPost, "/", nil) assert.NoError(t, err) ctx := req.Context() - rr, _, err := newReusableRequest(req, defaultMaxBodySize) + rr, _, err := newReusableRequest(req, true, defaultMaxBodySize) assert.NoError(t, err) // first call @@ -208,7 +250,7 @@ func TestCloneRequest(t *testing.T) { ctx := req.Context() req.ContentLength = int64(contentLength) - rr, _, err := newReusableRequest(req, defaultMaxBodySize) + rr, _, err := newReusableRequest(req, true, defaultMaxBodySize) assert.NoError(t, err) // first call @@ -231,7 +273,7 @@ func TestCloneRequest(t *testing.T) { req, err := http.NewRequest(http.MethodPost, "/", buf) assert.NoError(t, err) - _, expectedBytes, err := newReusableRequest(req, 2) + _, expectedBytes, err := newReusableRequest(req, true, 2) assert.Error(t, err) assert.Equal(t, expectedBytes, bb[:3]) }) @@ -243,7 +285,7 @@ func TestCloneRequest(t *testing.T) { req, err := http.NewRequest(http.MethodPost, "/", buf) assert.NoError(t, err) - rr, expectedBytes, err := newReusableRequest(req, 20) + rr, expectedBytes, err := newReusableRequest(req, true, 20) assert.NoError(t, err) assert.Nil(t, expectedBytes) assert.Len(t, rr.body, 10) @@ -255,14 +297,14 @@ func TestCloneRequest(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "/", buf) assert.NoError(t, err) - rr, expectedBytes, err := newReusableRequest(req, 20) + rr, expectedBytes, err := newReusableRequest(req, true, 20) assert.NoError(t, err) assert.Nil(t, expectedBytes) assert.Empty(t, rr.body) }) t.Run("no request given", func(t *testing.T) { - _, _, err := newReusableRequest(nil, defaultMaxBodySize) + _, _, err := newReusableRequest(nil, true, defaultMaxBodySize) assert.Error(t, err) }) } diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 247879bcf..46682e25a 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -22,6 +22,7 @@ import ( "github.com/traefik/traefik/v3/pkg/healthcheck" "github.com/traefik/traefik/v3/pkg/logs" "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" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/safe" @@ -34,7 +35,10 @@ import ( "google.golang.org/grpc/status" ) -const defaultMaxBodySize int64 = -1 +const ( + defaultMirrorBody = true + defaultMaxBodySize int64 = -1 +) // RoundTripperGetter is a roundtripper getter interface. type RoundTripperGetter interface { @@ -197,11 +201,16 @@ func (m *Manager) getMirrorServiceHandler(ctx context.Context, config *dynamic.M return nil, err } + mirrorBody := defaultMirrorBody + if config.MirrorBody != nil { + mirrorBody = *config.MirrorBody + } + maxBodySize := defaultMaxBodySize if config.MaxBodySize != nil { maxBodySize = *config.MaxBodySize } - handler := mirror.New(serviceHandler, m.routinePool, maxBodySize, config.HealthCheck) + handler := mirror.New(serviceHandler, m.routinePool, mirrorBody, maxBodySize, config.HealthCheck) for _, mirrorConfig := range config.Mirrors { mirrorHandler, err := m.BuildHTTP(ctx, mirrorConfig.Name) if err != nil { @@ -364,6 +373,17 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName proxy = observability.NewService(ctx, serviceName, proxy) } + if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName) { + // Some piece of middleware, like the ErrorPage, are relying on this serviceBuilder to get the handler for a given service, + // to re-target the request to it. + // Those pieces of middleware can be configured on routes that expose a Traefik internal service. + // In such a case, observability for internals being optional, the capture probe could be absent from context (no wrap via the entrypoint). + // But if the service targeted by this piece of middleware is not an internal one, + // and requires observability, we still want the capture probe to be present in the request context. + // Makes sure a capture probe is in the request context. + proxy, _ = capture.Wrap(proxy) + } + lb.Add(proxyName, proxy, server.Weight) // servers are considered UP by default.