Merge branch v3.6 into v3.7

This commit is contained in:
kevinpollet
2026-05-05 16:03:31 +02:00
28 changed files with 1921 additions and 11450 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ name: Build Web UI
on:
workflow_call: {}
env:
SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 72 # 3 days
SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 48 # 2 days
jobs:
build-webui:
+20
View File
@@ -1,3 +1,23 @@
## [v3.6.16](https://github.com/traefik/traefik/tree/v3.6.16) (2026-05-05)
[All Commits](https://github.com/traefik/traefik/compare/v3.6.15...v3.6.16)
**Bug fixes:**
- **[k8s/crd]** Remove cross-provider sanitization for Kubernetes service loading ([#13087](https://github.com/traefik/traefik/pull/13087) @rtribotte)
- **[k8s/ingress-nginx]** Fix typo in default CORS allowed headers ([#13088](https://github.com/traefik/traefik/pull/13088) @mliang2)
- **[logs, metrics, tracing]** Bump go.opentelemetry.io/otel ([#13100](https://github.com/traefik/traefik/pull/13100) @juliens)
- **[docker, ecs]** Migrate to github.com/moby/moby modules ([#12672](https://github.com/traefik/traefik/pull/12672) @thaJeztah)
- **[docker, ecs]** Migrate to github.com/moby/moby modules ([#13053](https://github.com/traefik/traefik/pull/13053) @mmatur)
**Documentation:**
- **[k8s/gatewayapi]** Update Helm chart values link for Kubernetes Gateway ([#13063](https://github.com/traefik/traefik/pull/13063) @0054)
## [v2.11.45](https://github.com/traefik/traefik/tree/v2.11.45) (2026-05-05)
[All Commits](https://github.com/traefik/traefik/compare/v2.11.44...v2.11.45)
**Bug fixes:**
- **[k8s/crd]** Remove cross-provider sanitization for Kubernetes service loading ([#13087](https://github.com/traefik/traefik/pull/13087) @rtribotte)
- **[docker, ecs]** Migrate to github.com/moby/moby modules ([#13053](https://github.com/traefik/traefik/pull/13053) @mmatur)
## [v3.7.0-rc.3](https://github.com/traefik/traefik/tree/v3.7.0-rc.3) (2026-04-29)
[All Commits](https://github.com/traefik/traefik/compare/v3.7.0-rc.2...v3.7.0-rc.3)
+10
View File
@@ -80,6 +80,16 @@ Note: TLSOptions for `HostRegexp` matchers remains unsupported. Use wildcard `Ho
---
## v3.6.16
### Docker provider: minimum Docker Engine version
Starting with `v3.6.16`, the Docker provider requires Docker API version v1.40 or
above ([Docker Engine v19.03](https://docs.docker.com/reference/api/engine/#api-version-matrix)).
Users running older (end of life) versions of Docker Engine should update their
Docker Engine or use the [`DOCKER_API_VERSION`](https://docs.docker.com/reference/api/engine/#versioned-api-and-sdk)
environment variable to override the API version used by Traefik.
## v3.6.15
In `v3.6.15`, a new `errorRequestHeaders` option has been added to the Errors middleware.
@@ -17,7 +17,7 @@ For more details, check out the conformance [report](https://github.com/kubernet
!!! info "Using The Helm Chart"
When using the Traefik [Helm Chart](../../../../getting-started/kubernetes.md#install-traefik), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you.
The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L323).
The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml).
## Requirements
+23 -23
View File
@@ -19,8 +19,7 @@ require (
github.com/containerd/errdefs v1.0.0
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo.
github.com/coreos/go-systemd/v22 v22.5.0
github.com/docker/cli v29.2.1+incompatible
github.com/docker/docker v28.5.2+incompatible
github.com/docker/cli v29.4.0+incompatible
github.com/docker/go-connections v0.6.0
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.9.0
@@ -51,6 +50,8 @@ require (
github.com/mitchellh/copystructure v1.2.0
github.com/mitchellh/hashstructure v1.0.0
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // No tag on the repo.
github.com/moby/moby/api v1.54.1
github.com/moby/moby/client v0.4.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pires/go-proxyproto v0.8.1
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo.
@@ -59,15 +60,15 @@ require (
github.com/quic-go/quic-go v0.59.0
github.com/redis/go-redis/v9 v9.8.0
github.com/rs/zerolog v1.33.0
github.com/sirupsen/logrus v1.9.3
github.com/sirupsen/logrus v1.9.4
github.com/spiffe/go-spiffe/v2 v2.6.0
github.com/stealthrocket/wasi-go v0.8.0
github.com/stealthrocket/wazergo v0.19.1
github.com/stretchr/testify v1.11.1
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807 // No tag on the repo.
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // No tag on the repo.
github.com/testcontainers/testcontainers-go v0.40.0
github.com/testcontainers/testcontainers-go/modules/k3s v0.40.0
github.com/testcontainers/testcontainers-go v0.42.0
github.com/testcontainers/testcontainers-go/modules/k3s v0.42.0
github.com/tetratelabs/wazero v1.8.0
github.com/tidwall/gjson v1.17.0
github.com/traefik/grpc-web v0.16.0
@@ -85,17 +86,17 @@ require (
go.opentelemetry.io/contrib/bridges/otellogrus v0.13.0
go.opentelemetry.io/contrib/propagators/autoprop v0.63.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.17.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.17.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0
go.opentelemetry.io/otel/log v0.17.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
go.opentelemetry.io/otel/log v0.19.0
go.opentelemetry.io/otel/metric v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/sdk/log v0.17.0
go.opentelemetry.io/otel/sdk/log v0.19.0
go.opentelemetry.io/otel/sdk/metric v1.43.0
go.opentelemetry.io/otel/trace v1.43.0
golang.org/x/crypto v0.50.0
@@ -198,7 +199,7 @@ require (
github.com/distribution/reference v0.6.0 // indirect
github.com/dnsimple/dnsimple-go/v4 v4.0.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/exoscale/egoscale/v3 v3.1.34 // indirect
@@ -295,8 +296,8 @@ require (
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
@@ -304,7 +305,6 @@ require (
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/namedotcom/go/v4 v4.0.2 // indirect
@@ -349,7 +349,7 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 // indirect
github.com/selectel/domains-go v1.1.0 // indirect
github.com/selectel/go-selvpcclient/v4 v4.2.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/softlayer/softlayer-go v1.2.1 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
@@ -365,8 +365,8 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/transip/gotransip/v6 v6.26.2 // indirect
github.com/ucloud/ucloud-sdk-go v0.22.63 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 // indirect
@@ -391,7 +391,7 @@ require (
go.opentelemetry.io/contrib/propagators/b3 v1.38.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.38.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
@@ -404,7 +404,7 @@ require (
golang.org/x/term v0.42.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/api v0.276.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
+52 -53
View File
@@ -915,8 +915,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -939,10 +939,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dnsimple/dnsimple-go/v4 v4.0.0 h1:nUCICZSyZDiiqimAAL+E8XL+0sKGks5VRki5S8XotRo=
github.com/dnsimple/dnsimple-go/v4 v4.0.0/go.mod h1:AXT2yfAFOntJx6iMeo1J/zKBw0ggXFYBt4e97dqqPnc=
github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg=
github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -953,8 +951,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
@@ -1623,14 +1621,16 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
@@ -1648,8 +1648,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -1866,8 +1864,8 @@ github.com/selectel/domains-go v1.1.0 h1:futG50J43ALLKQAnZk9H9yOtLGnSUh7c5hSvuC5
github.com/selectel/domains-go v1.1.0/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA=
github.com/selectel/go-selvpcclient/v4 v4.2.0 h1:tVqSAdmNcdshv3AgfaIwGHs1oLk4jX8Tm+ccMg1rBmc=
github.com/selectel/go-selvpcclient/v4 v4.2.0/go.mod h1:eFhL1KUW159KOJVeGO7k/Uxl0TYd/sBkWXjuF5WxmYk=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
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=
@@ -1880,8 +1878,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -1967,10 +1965,10 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24/go.mod h
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.38/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.83 h1:C8ro7XQVV17O+A7zUTe28VK02NuyazuaY0CB2CH5Scw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.83/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU=
github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=
github.com/testcontainers/testcontainers-go/modules/k3s v0.40.0 h1:3w6SjtIp/+FdpjWJCyPqaGWknG2iU6MacEWA7hl0IqQ=
github.com/testcontainers/testcontainers-go/modules/k3s v0.40.0/go.mod h1:1xJwmfO2g+XKox9LiJXKGCm1vWp7LozX+78UjXVRbF0=
github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY=
github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30=
github.com/testcontainers/testcontainers-go/modules/k3s v0.42.0 h1:bTVmcnYaSHesN6HXXxV/k0+BMkyfo3VBy4w4yRqOIgE=
github.com/testcontainers/testcontainers-go/modules/k3s v0.42.0/go.mod h1:2O8+V4WzMb/bjg/Sez+aYci9LpGUbT5cSz7ildfTIb8=
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
@@ -1987,10 +1985,10 @@ github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxm
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY=
github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4=
@@ -2124,32 +2122,32 @@ go.opentelemetry.io/contrib/propagators/ot v1.38.0 h1:k4gSyyohaDXI8F9BDXYC3uO2vr
go.opentelemetry.io/contrib/propagators/ot v1.38.0/go.mod h1:2hDsuiHRO39SRUMhYGqmj64z/IuMRoxE4bBSFR82Lo8=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.17.0 h1:6SRrIZrFLFVkktXaO0OUTweDdxNveqxczTsk3XUVQX8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.17.0/go.mod h1:Nx2rIwEusIh/KFV8UrjjB87BfVn+daJ/lWCA0CkxAtY=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.17.0 h1:GcSx2UgcMuQEu0vHq823xR5LCN3WqEx5yKhqDkv1pwY=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.17.0/go.mod h1:ctNT8t8Vzx9sb1oWAozighT3guWorr8xdCboBvkT5yg=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0 h1:VO3BL6OZXRQ1yQc8W6EVfJzINeJ35BkiHx4MYfoQf44=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.41.0/go.mod h1:qRDnJ2nv3CQXMK2HUd9K9VtvedsPAce3S+/4LZHjX/s=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0 h1:MMrOAN8H1FrvDyq9UJ4lu5/+ss49Qgfgb7Zpm0m8ABo=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.41.0/go.mod h1:Na+2NNASJtF+uT4NxDe0G+NQb+bUgdPDfwxY/6JmS/c=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9ZhgqiDqh6BioM7DVHHzEGVq76Db5897WLGZ5Go=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY=
go.opentelemetry.io/otel/log v0.17.0 h1:blZWM4y7n+KSa9OywwGWyBMPpeVoCl/NCw+jMps8afM=
go.opentelemetry.io/otel/log v0.17.0/go.mod h1:VXhjKYep6/laSgf/tjdh2SMAt18Z9XotBFBO0jxSE24=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A=
go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/log v0.17.0 h1:stWOgJB8bWieSlX4VO+gD7BrRZ/Dh1H/u7115amleGE=
go.opentelemetry.io/otel/sdk/log v0.17.0/go.mod h1:LQKPUyHraLka2sRvNQ5+W456+sElomqR7VWpOnOefZg=
go.opentelemetry.io/otel/sdk/log/logtest v0.17.0 h1:Z4S9W5piCH88itCkWDtX5ppRgO0UTkLXVK/6tPOMM2w=
go.opentelemetry.io/otel/sdk/log/logtest v0.17.0/go.mod h1:d9iIX/BwLfu1BTPxO0wi4ucyCenCckfuf9LC0aJDjqM=
go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
@@ -2157,8 +2155,8 @@ go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLh
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.opentelemetry.io/proto/slim/otlp v1.7.1 h1:lZ11gEokjIWYM3JWOUrIILr2wcf6RX+rq5SPObV9oyc=
go.opentelemetry.io/proto/slim/otlp v1.7.1/go.mod h1:uZ6LJWa49eNM/EXnnvJGTTu8miokU8RQdnO980LJ57g=
go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.0.1 h1:Tr/eXq6N7ZFjN+THBF/BtGLUz8dciA7cuzGRsCEkZ88=
@@ -2552,7 +2550,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2933,8 +2930,8 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -3110,6 +3107,8 @@ mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+19 -13
View File
@@ -11,6 +11,7 @@ import (
"io/fs"
stdlog "log"
"net/http"
"net/netip"
"os"
"os/exec"
"path/filepath"
@@ -22,10 +23,11 @@ import (
"text/template"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
dockernetwork "github.com/docker/docker/api/types/network"
"github.com/fatih/structs"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/mount"
dockernetwork "github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@@ -97,7 +99,7 @@ func (s *BaseSuite) SetupSuite() {
Driver: "default",
Config: []dockernetwork.IPAMConfig{
{
Subnet: "172.31.42.0/24",
Subnet: netip.MustParsePrefix("172.31.42.0/24"),
},
},
}))
@@ -150,12 +152,12 @@ func isDockerDesktop(t *testing.T) bool {
t.Fatalf("failed to create docker client: %s", err)
}
info, err := cli.Info(t.Context())
res, err := cli.Info(t.Context(), client.InfoOptions{})
if err != nil {
t.Fatalf("failed to get docker info: %s", err)
}
return info.OperatingSystem == "Docker Desktop"
return res.Info.OperatingSystem == "Docker Desktop"
}
func (s *BaseSuite) TearDownSuite() {
@@ -195,24 +197,28 @@ func (s *BaseSuite) createComposeProject(name string) {
for id, containerConfig := range composeConfigData.Services {
var mounts []mount.Mount
for _, volume := range containerConfig.Volumes {
split := strings.Split(volume, ":")
if len(split) != 2 {
src, dst, ok := strings.Cut(volume, ":")
if !ok {
continue
}
if strings.HasPrefix(split[0], "./") {
path, err := os.Getwd()
if strings.HasPrefix(src, "./") {
wd, err := os.Getwd()
if err != nil {
log.Err(err).Msg("can't determine current directory")
continue
}
split[0] = strings.Replace(split[0], "./", path+"/", 1)
src = wd + "/" + strings.TrimPrefix(src, "./")
}
abs, err := filepath.Abs(split[0])
abs, err := filepath.Abs(src)
require.NoError(s.T(), err)
mounts = append(mounts, mount.Mount{Source: abs, Target: split[1], Type: mount.TypeBind})
mounts = append(mounts, mount.Mount{
Source: abs,
Target: dst,
Type: mount.TypeBind,
})
}
if containerConfig.Deploy.Replicas > 0 {
+39 -22
View File
@@ -1,19 +1,18 @@
package docker
import (
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
"net/netip"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
)
func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes.InspectResponse {
c := &containertypes.InspectResponse{
ContainerJSONBase: &containertypes.ContainerJSONBase{
Name: "fake",
HostConfig: &containertypes.HostConfig{},
State: &containertypes.State{},
},
Name: "fake",
HostConfig: &containertypes.HostConfig{},
State: &containertypes.State{},
Config: &containertypes.Config{},
NetworkSettings: &containertypes.NetworkSettings{},
}
@@ -27,17 +26,17 @@ func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes.
func name(name string) func(*containertypes.InspectResponse) {
return func(c *containertypes.InspectResponse) {
c.ContainerJSONBase.Name = name
c.Name = name
}
}
func networkMode(mode string) func(*containertypes.InspectResponse) {
return func(c *containertypes.InspectResponse) {
c.ContainerJSONBase.HostConfig.NetworkMode = containertypes.NetworkMode(mode)
c.HostConfig.NetworkMode = containertypes.NetworkMode(mode)
}
}
func ports(portMap nat.PortMap) func(*containertypes.InspectResponse) {
func ports(portMap networktypes.PortMap) func(*containertypes.InspectResponse) {
return func(c *containertypes.InspectResponse) {
c.NetworkSettings.Ports = portMap
}
@@ -57,13 +56,13 @@ func withNetwork(name string, ops ...func(*networktypes.EndpointSettings)) func(
func ipv4(ip string) func(*networktypes.EndpointSettings) {
return func(s *networktypes.EndpointSettings) {
s.IPAddress = ip
s.IPAddress = netip.MustParseAddr(ip).Unmap()
}
}
func ipv6(ip string) func(*networktypes.EndpointSettings) {
return func(s *networktypes.EndpointSettings) {
s.GlobalIPv6Address = ip
s.GlobalIPv6Address = netip.MustParseAddr(ip)
}
}
@@ -92,20 +91,20 @@ func taskNodeID(id string) func(*swarmtypes.Task) {
}
func taskNetworkAttachment(id, name, driver string, addresses []string) func(*swarmtypes.Task) {
prefixes := make([]netip.Prefix, len(addresses))
for i, s := range addresses {
prefixes[i] = mustParseAddrOrPrefix(s)
}
return func(task *swarmtypes.Task) {
task.NetworksAttachments = append(task.NetworksAttachments, swarmtypes.NetworkAttachment{
Network: swarmtypes.Network{
ID: id,
Spec: swarmtypes.NetworkSpec{
Annotations: swarmtypes.Annotations{
Name: name,
},
DriverConfiguration: &swarmtypes.Driver{
Name: driver,
},
Annotations: swarmtypes.Annotations{Name: name},
DriverConfiguration: &swarmtypes.Driver{Name: driver},
},
},
Addresses: addresses,
Addresses: prefixes,
})
}
}
@@ -184,7 +183,7 @@ func virtualIP(networkID, addr string) func(*swarmtypes.Endpoint) {
}
endpoint.VirtualIPs = append(endpoint.VirtualIPs, swarmtypes.EndpointVirtualIP{
NetworkID: networkID,
Addr: addr,
Addr: mustParseAddrOrPrefix(addr),
})
}
}
@@ -208,3 +207,21 @@ func modeDNSRR(spec *swarmtypes.EndpointSpec) {
func modeVIP(spec *swarmtypes.EndpointSpec) {
spec.Mode = swarmtypes.ResolutionModeVIP
}
// mustParseAddrOrPrefix parses addrOrPrefix into a [netip.Prefix].
//
// We should expect only IP-addresses, but for backwards-compatibility,
// the Addresses field on [swarmtypes.NetworkAttachment] accepts a prefix.
func mustParseAddrOrPrefix(addrOrPrefix string) netip.Prefix {
if addrOrPrefix == "" {
return netip.Prefix{}
}
if p, err := netip.ParsePrefix(addrOrPrefix); err == nil {
return p
}
a := netip.MustParseAddr(addrOrPrefix)
if a.Is4() {
return netip.PrefixFrom(a, 32)
}
return netip.PrefixFrom(a, 128)
}
+10 -10
View File
@@ -7,9 +7,9 @@ import (
"net"
"strings"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/config/label"
@@ -335,10 +335,10 @@ func (p *DynConfBuilder) getIPPort(ctx context.Context, container dockerData, se
switch {
case err != nil:
logger.Info().Msgf("Unable to find a binding for container %q, falling back on its internal IP/Port.", container.Name)
case portBinding.HostIP == "0.0.0.0" || len(portBinding.HostIP) == 0:
case portBinding.HostIP.IsUnspecified() || !portBinding.HostIP.IsValid():
logger.Info().Msgf("Cannot determine the IP address (got %q) for %q's binding, falling back on its internal IP/Port.", portBinding.HostIP, container.Name)
default:
ip = portBinding.HostIP
ip = portBinding.HostIP.String()
port = portBinding.HostPort
usedBound = true
}
@@ -387,7 +387,7 @@ func (p *DynConfBuilder) getIPAddress(ctx context.Context, container dockerData)
if container.NetworkSettings.NetworkMode.IsContainer() {
connectedContainer := container.NetworkSettings.NetworkMode.ConnectedContainer()
containerInspected, err := p.apiClient.ContainerInspect(context.Background(), connectedContainer)
res, err := p.apiClient.ContainerInspect(context.Background(), connectedContainer, client.ContainerInspectOptions{})
if err != nil {
logger.Warn().Err(err).Msgf("Unable to get IP address for container %s: failed to inspect container ID %s", container.Name, connectedContainer)
return ""
@@ -395,10 +395,10 @@ func (p *DynConfBuilder) getIPAddress(ctx context.Context, container dockerData)
// Check connected container for traefik.docker.network,
// falling back to the network specified on the current container.
containerParsed := parseContainer(containerInspected)
containerParsed := parseContainer(res.Container)
extraConf, err := p.extractLabels(containerParsed)
if err != nil {
logger.Warn().Err(err).Msgf("Unable to get IP address for container %s: failed to get extra configuration for container %s", container.Name, containerInspected.Name)
logger.Warn().Err(err).Msgf("Unable to get IP address for container %s: failed to get extra configuration for container %s", container.Name, res.Container.Name)
return ""
}
@@ -424,11 +424,11 @@ func (p *DynConfBuilder) getIPAddress(ctx context.Context, container dockerData)
return ""
}
func (p *DynConfBuilder) getPortBinding(container dockerData, serverPort string) (*nat.PortBinding, error) {
func (p *DynConfBuilder) getPortBinding(container dockerData, serverPort string) (*networktypes.PortBinding, error) {
port := getPort(container, serverPort)
for netPort, portBindings := range container.NetworkSettings.Ports {
if strings.EqualFold(string(netPort), port+"/TCP") || strings.EqualFold(string(netPort), port+"/UDP") {
if netPort.Port() == port && (netPort.Proto() == networktypes.TCP || netPort.Proto() == networktypes.UDP) {
for _, p := range portBindings {
return &p, nil
}
+186 -186
View File
@@ -1,14 +1,14 @@
package docker
import (
"net/netip"
"strconv"
"testing"
"time"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ptypes "github.com/traefik/paerser/types"
@@ -33,8 +33,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -97,8 +97,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -163,8 +163,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
"traefik.domain": "foo.bar",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -227,8 +227,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -285,8 +285,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -343,8 +343,8 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -447,8 +447,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.test": "",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -491,8 +491,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.services.test": "",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -535,8 +535,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.udp.services.test": "",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/udp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/udp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -577,8 +577,8 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -640,8 +640,8 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -656,8 +656,8 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test2",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -739,8 +739,8 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -756,8 +756,8 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -824,8 +824,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -891,8 +891,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.service": "Service1",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -955,8 +955,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1020,8 +1020,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1086,8 +1086,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service2.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1160,8 +1160,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.service": "Service1",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1225,8 +1225,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1244,8 +1244,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "false",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1295,8 +1295,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "false",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1314,8 +1314,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1333,8 +1333,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1384,8 +1384,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1403,8 +1403,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1471,8 +1471,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1543,8 +1543,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1562,8 +1562,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1637,8 +1637,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1656,8 +1656,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "41",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1725,8 +1725,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1744,8 +1744,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "41",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1763,8 +1763,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "40",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1835,8 +1835,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1854,8 +1854,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`bar.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1917,8 +1917,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1936,8 +1936,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`bar.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -1955,8 +1955,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foobar.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2021,8 +2021,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2040,8 +2040,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2107,8 +2107,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2125,8 +2125,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Router1.rule": "Host(`foo.com`)",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2198,8 +2198,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.wrong.label": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2264,8 +2264,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.LoadBalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2330,8 +2330,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service2.LoadBalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2403,8 +2403,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.LoadBalancer.server.url": "http://1.2.3.4:5678",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("4567/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("4567/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2469,8 +2469,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.LoadBalancer.server.preservepath": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("4567/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("4567/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2536,8 +2536,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.LoadBalancer.server.port": "1234",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("4567/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("4567/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2581,8 +2581,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.LoadBalancer.server.scheme": "https",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("4567/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("4567/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2623,7 +2623,7 @@ func TestDynConfBuilder_build(t *testing.T) {
Name: "Test",
Labels: map[string]string{},
NetworkSettings: networkSettings{
Ports: nat.PortMap{},
Ports: networktypes.PortMap{},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
@@ -2665,7 +2665,7 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.middlewares.Middleware1.inflightreq.amount": "42",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{},
Ports: networktypes.PortMap{},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
@@ -2707,8 +2707,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.enable": "false",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -2985,8 +2985,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tags": "foo",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3030,8 +3030,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tags": "foo",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3097,8 +3097,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.routers.Test.middlewares": "Middleware1",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3174,8 +3174,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.routers.Test.middlewares": "Middleware1",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3241,8 +3241,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.routers.foo.tls": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3301,8 +3301,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.udp.routers.foo.entrypoints": "mydns",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3360,8 +3360,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.routers.foo.tls": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3416,8 +3416,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3479,8 +3479,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.udp.services.foo.loadbalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3540,8 +3540,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3561,8 +3561,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3647,8 +3647,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.udp.services.foo.loadbalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3702,8 +3702,8 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("80/tcp"): []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -3756,13 +3756,13 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.http.services.Test.loadbalancer.server.port": "80",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("79/tcp"): []nat.PortBinding{{
HostIP: "192.168.0.1",
Ports: networktypes.PortMap{
networktypes.MustParsePort("79/tcp"): []networktypes.PortBinding{{
HostIP: netip.MustParseAddr("192.168.0.1"),
HostPort: "8080",
}},
nat.Port("80/tcp"): []nat.PortBinding{{
HostIP: "192.168.0.1",
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{{
HostIP: netip.MustParseAddr("192.168.0.1"),
HostPort: "8081",
}},
},
@@ -3831,13 +3831,13 @@ func TestDynConfBuilder_build(t *testing.T) {
"traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar",
},
NetworkSettings: networkSettings{
Ports: nat.PortMap{
nat.Port("79/tcp"): []nat.PortBinding{{
HostIP: "192.168.0.1",
Ports: networktypes.PortMap{
networktypes.MustParsePort("79/tcp"): []networktypes.PortBinding{{
HostIP: netip.MustParseAddr("192.168.0.1"),
HostPort: "8080",
}},
nat.Port("80/tcp"): []nat.PortBinding{{
HostIP: "192.168.0.1",
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{{
HostIP: netip.MustParseAddr("192.168.0.1"),
HostPort: "8081",
}},
},
@@ -3956,8 +3956,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4020,8 +4020,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4068,8 +4068,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4137,8 +4137,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4201,8 +4201,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4268,8 +4268,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4328,8 +4328,8 @@ func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/udp": []nat.PortBinding{},
Ports: networktypes.PortMap{
networktypes.MustParsePort("80/udp"): []networktypes.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
@@ -4410,8 +4410,8 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port not set, no binding, falling back on the container's IP/Port",
container: containerJSON(
ports(nat.PortMap{
"8080/tcp": {},
ports(networktypes.PortMap{
networktypes.MustParsePort("8080/tcp"): {},
}),
withNetwork("testnet", ipv4("10.11.12.13"))),
expected: expected{
@@ -4423,8 +4423,8 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
desc: "label traefik.port not set, single binding with port only, falling back on the container's IP/Port",
container: containerJSON(
withNetwork("testnet", ipv4("10.11.12.13")),
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
{
HostPort: "8082",
},
@@ -4439,10 +4439,10 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port not set, binding with ip:port should create a route to the bound ip:port",
container: containerJSON(
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
{
HostIP: "1.2.3.4",
HostIP: netip.MustParseAddr("1.2.3.4"),
HostPort: "8081",
},
},
@@ -4465,10 +4465,10 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port set, single binding with ip:port for the label, creates the route",
container: containerJSON(
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("443/tcp"): []networktypes.PortBinding{
{
HostIP: "5.6.7.8",
HostIP: netip.MustParseAddr("5.6.7.8"),
HostPort: "8082",
},
},
@@ -4483,10 +4483,10 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port set, no binding on the corresponding port, falling back on the container's IP/label.port",
container: containerJSON(
ports(nat.PortMap{
"443/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("443/tcp"): []networktypes.PortBinding{
{
HostIP: "5.6.7.8",
HostIP: netip.MustParseAddr("5.6.7.8"),
HostPort: "8082",
},
},
@@ -4501,16 +4501,16 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port set, multiple bindings on different ports, uses the label to select the correct (first) binding",
container: containerJSON(
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
{
HostIP: "1.2.3.4",
HostIP: netip.MustParseAddr("1.2.3.4"),
HostPort: "8081",
},
},
"443/tcp": []nat.PortBinding{
networktypes.MustParsePort("443/tcp"): []networktypes.PortBinding{
{
HostIP: "5.6.7.8",
HostIP: netip.MustParseAddr("5.6.7.8"),
HostPort: "8082",
},
},
@@ -4525,16 +4525,16 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
{
desc: "label traefik.port set, multiple bindings on different ports, uses the label to select the correct (second) binding",
container: containerJSON(
ports(nat.PortMap{
"80/tcp": []nat.PortBinding{
ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): []networktypes.PortBinding{
{
HostIP: "1.2.3.4",
HostIP: netip.MustParseAddr("1.2.3.4"),
HostPort: "8081",
},
},
"443/tcp": []nat.PortBinding{
networktypes.MustParsePort("443/tcp"): []networktypes.PortBinding{
{
HostIP: "5.6.7.8",
HostIP: netip.MustParseAddr("5.6.7.8"),
HostPort: "8082",
},
},
@@ -4706,7 +4706,7 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) {
expected: "10.11.12.13",
networks: map[string]*networktypes.Summary{
"1": {
Name: "foo",
Network: networktypes.Network{Name: "foo"},
},
},
},
@@ -4724,10 +4724,10 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) {
expected: "10.11.12.99",
networks: map[string]*networktypes.Summary{
"1": {
Name: "foonet",
Network: networktypes.Network{Name: "foonet"},
},
"2": {
Name: "barnet",
Network: networktypes.Network{Name: "barnet"},
},
},
},
+5 -5
View File
@@ -1,8 +1,8 @@
package docker
import (
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
)
// dockerData holds the need data to the provider.
@@ -10,10 +10,10 @@ type dockerData struct {
ID string
ServiceName string
Name string
Status string
Status containertypes.ContainerState
Labels map[string]string // List of labels set to container or service
NetworkSettings networkSettings
Health string
Health containertypes.HealthStatus
NodeIP string // Only filled in Swarm mode.
ExtraConf configuration
}
@@ -21,7 +21,7 @@ type dockerData struct {
// NetworkSettings holds the networks data to the provider.
type networkSettings struct {
NetworkMode containertypes.NetworkMode
Ports nat.PortMap
Ports networktypes.PortMap
Networks map[string]*networkData
}
+10 -16
View File
@@ -9,10 +9,8 @@ import (
"time"
"github.com/cenkalti/backoff/v4"
"github.com/docker/docker/api/types/container"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
eventtypes "github.com/moby/moby/api/types/events"
"github.com/moby/moby/client"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/job"
@@ -73,7 +71,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
builder := NewDynConfBuilder(p.Shared, dockerClient, false)
serverVersion, err := dockerClient.ServerVersion(ctx)
serverVersion, err := dockerClient.ServerVersion(ctx, client.ServerVersionOptions{})
if err != nil {
logger.Error().Err(err).Msg("Failed to retrieve information of the docker client and server host")
return err
@@ -94,12 +92,6 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
}
if p.Watch {
f := filters.NewArgs()
f.Add("type", "container")
options := eventtypes.ListOptions{
Filters: f,
}
startStopHandle := func(m eventtypes.Message) {
logger.Debug().Msgf("Provider event received %+v", m)
containers, err := p.listContainers(ctx, dockerClient)
@@ -122,16 +114,18 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
}
}
eventsc, errc := dockerClient.Events(ctx, options)
res := dockerClient.Events(ctx, client.EventsListOptions{
Filters: make(client.Filters).Add("type", string(eventtypes.ContainerEventType)),
})
for {
select {
case event := <-eventsc:
case event := <-res.Messages:
if event.Action == "start" ||
event.Action == "die" ||
strings.HasPrefix(string(event.Action), "health_status") {
startStopHandle(event)
}
case err := <-errc:
case err := <-res.Err:
if errors.Is(err, io.EOF) {
logger.Debug().Msg("Provider event stream closed")
}
@@ -162,7 +156,7 @@ func (p *Provider) createClient(ctx context.Context) (*client.Client, error) {
}
func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) {
containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{
containerList, err := dockerClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
})
if err != nil {
@@ -171,7 +165,7 @@ func (p *Provider) listContainers(ctx context.Context, dockerClient client.Conta
var inspectedContainers []dockerData
// get inspect containers
for _, c := range containerList {
for _, c := range containerList.Items {
dData := inspectContainers(ctx, dockerClient, c.ID)
if len(dData.Name) == 0 {
continue
+26 -26
View File
@@ -3,16 +3,14 @@ package docker
import (
"context"
"fmt"
"net"
"strconv"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/docker/docker/api/types/filters"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/versions"
"github.com/rs/zerolog/log"
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
@@ -77,7 +75,7 @@ func (p *SwarmProvider) Provide(configurationChan chan<- dynamic.Message, pool *
builder := NewDynConfBuilder(p.Shared, dockerClient, true)
serverVersion, err := dockerClient.ServerVersion(ctx)
serverVersion, err := dockerClient.ServerVersion(ctx, client.ServerVersionOptions{})
if err != nil {
logger.Error().Err(err).Msg("Failed to retrieve information of the docker client and server host")
return err
@@ -158,17 +156,17 @@ func (p *SwarmProvider) createClient(ctx context.Context) (*client.Client, error
func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerData, error) {
logger := log.Ctx(ctx)
serviceList, err := dockerClient.ServiceList(ctx, swarmtypes.ServiceListOptions{})
serviceList, err := dockerClient.ServiceList(ctx, client.ServiceListOptions{})
if err != nil {
return nil, err
}
serverVersion, err := dockerClient.ServerVersion(ctx)
serverVersion, err := dockerClient.ServerVersion(ctx, client.ServerVersionOptions{})
if err != nil {
return nil, err
}
networkListArgs := filters.NewArgs()
networkListArgs := client.Filters{}
// https://docs.docker.com/engine/api/v1.29/#tag/Network (Docker 17.06)
if versions.GreaterThanOrEqualTo(serverVersion.APIVersion, "1.29") {
networkListArgs.Add("scope", "swarm")
@@ -176,21 +174,23 @@ func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.AP
networkListArgs.Add("driver", "overlay")
}
networkList, err := dockerClient.NetworkList(ctx, networktypes.ListOptions{Filters: networkListArgs})
networkList, err := dockerClient.NetworkList(ctx, client.NetworkListOptions{
Filters: networkListArgs,
})
if err != nil {
logger.Debug().Err(err).Msg("Failed to network inspect on client for docker")
return nil, err
}
networkMap := make(map[string]*networktypes.Summary)
for _, network := range networkList {
for _, network := range networkList.Items {
networkMap[network.ID] = &network
}
var dockerDataList []dockerData
var dockerDataListTasks []dockerData
for _, service := range serviceList {
for _, service := range serviceList.Items {
dData, err := p.parseService(ctx, service, networkMap)
if err != nil {
logger.Error().Err(err).Msgf("Skip container %s", getServiceName(dData))
@@ -247,15 +247,14 @@ func (p *SwarmProvider) parseService(ctx context.Context, service swarmtypes.Ser
logger.Debug().Msgf("Network not found, id: %s", virtualIP.NetworkID)
continue
}
if len(virtualIP.Addr) == 0 {
if !virtualIP.Addr.Addr().IsValid() {
logger.Debug().Msgf("No virtual IPs found in network %s", virtualIP.NetworkID)
continue
}
ip, _, _ := net.ParseCIDR(virtualIP.Addr)
network := &networkData{
Name: networkService.Name,
ID: virtualIP.NetworkID,
Addr: ip.String(),
Addr: virtualIP.Addr.Addr().String(),
}
dData.NetworkSettings.Networks[network.Name] = network
}
@@ -266,17 +265,15 @@ func (p *SwarmProvider) parseService(ctx context.Context, service swarmtypes.Ser
func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID string,
serviceDockerData dockerData, networkMap map[string]*networktypes.Summary, isGlobalSvc bool,
) ([]dockerData, error) {
serviceIDFilter := filters.NewArgs()
serviceIDFilter.Add("service", serviceID)
serviceIDFilter.Add("desired-state", "running")
taskList, err := dockerClient.TaskList(ctx, swarmtypes.TaskListOptions{Filters: serviceIDFilter})
taskList, err := dockerClient.TaskList(ctx, client.TaskListOptions{
Filters: make(client.Filters).Add("service", serviceID).Add("desired-state", "running"),
})
if err != nil {
return nil, err
}
var dockerDataList []dockerData
for _, task := range taskList {
for _, task := range taskList.Items {
if task.Status.State != swarmtypes.TaskStateRunning {
continue
}
@@ -309,11 +306,11 @@ func parseTasks(ctx context.Context, dockerClient client.APIClient, task swarmty
}
if task.NodeID != "" {
node, _, err := dockerClient.NodeInspectWithRaw(ctx, task.NodeID)
res, err := dockerClient.NodeInspect(ctx, task.NodeID, client.NodeInspectOptions{})
if err != nil {
return dockerData{}, fmt.Errorf("inspecting node %s: %w", task.NodeID, err)
}
dData.NodeIP = node.Status.Addr
dData.NodeIP = res.Node.Status.Addr
}
if task.NetworksAttachments != nil {
@@ -323,11 +320,14 @@ func parseTasks(ctx context.Context, dockerClient client.APIClient, task swarmty
if len(virtualIP.Addresses) > 0 {
// Not sure about this next loop - when would a task have multiple IP's for the same network?
for _, addr := range virtualIP.Addresses {
ip, _, _ := net.ParseCIDR(addr)
var ip string
if addr.IsValid() {
ip = addr.Addr().String()
}
network := &networkData{
ID: virtualIP.Network.ID,
Name: networkService.Name,
Addr: ip.String(),
Addr: ip,
}
dData.NetworkSettings.Networks[network.Name] = network
}
+23 -22
View File
@@ -3,31 +3,30 @@ package docker
import (
"context"
dockertypes "github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
dockerclient "github.com/docker/docker/client"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
)
type fakeTasksClient struct {
dockerclient.APIClient
client.APIClient
tasks []swarmtypes.Task
container containertypes.InspectResponse
err error
}
func (c *fakeTasksClient) TaskList(ctx context.Context, options swarmtypes.TaskListOptions) ([]swarmtypes.Task, error) {
return c.tasks, c.err
func (c *fakeTasksClient) TaskList(ctx context.Context, options client.TaskListOptions) (client.TaskListResult, error) {
return client.TaskListResult{Items: c.tasks}, c.err
}
func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string) (containertypes.InspectResponse, error) {
return c.container, c.err
func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string, options client.ContainerInspectOptions) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{Container: c.container}, c.err
}
type fakeServicesClient struct {
dockerclient.APIClient
client.APIClient
dockerVersion string
networks []networktypes.Summary
@@ -37,27 +36,29 @@ type fakeServicesClient struct {
err error
}
func (c *fakeServicesClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarmtypes.Node, []byte, error) {
func (c *fakeServicesClient) NodeInspect(ctx context.Context, nodeID string, options client.NodeInspectOptions) (client.NodeInspectResult, error) {
for _, node := range c.nodes {
if node.ID == nodeID {
return node, nil, nil
return client.NodeInspectResult{
Node: node,
}, nil
}
}
return swarmtypes.Node{}, nil, c.err
return client.NodeInspectResult{}, c.err
}
func (c *fakeServicesClient) ServiceList(ctx context.Context, options swarmtypes.ServiceListOptions) ([]swarmtypes.Service, error) {
return c.services, c.err
func (c *fakeServicesClient) ServiceList(ctx context.Context, options client.ServiceListOptions) (client.ServiceListResult, error) {
return client.ServiceListResult{Items: c.services}, c.err
}
func (c *fakeServicesClient) ServerVersion(ctx context.Context) (dockertypes.Version, error) {
return dockertypes.Version{APIVersion: c.dockerVersion}, c.err
func (c *fakeServicesClient) ServerVersion(ctx context.Context, options client.ServerVersionOptions) (client.ServerVersionResult, error) {
return client.ServerVersionResult{APIVersion: c.dockerVersion}, c.err
}
func (c *fakeServicesClient) NetworkList(ctx context.Context, options networktypes.ListOptions) ([]networktypes.Summary, error) {
return c.networks, c.err
func (c *fakeServicesClient) NetworkList(ctx context.Context, options client.NetworkListOptions) (client.NetworkListResult, error) {
return client.NetworkListResult{Items: c.networks}, c.err
}
func (c *fakeServicesClient) TaskList(ctx context.Context, options swarmtypes.TaskListOptions) ([]swarmtypes.Task, error) {
return c.tasks, c.err
func (c *fakeServicesClient) TaskList(ctx context.Context, options client.TaskListOptions) (client.TaskListResult, error) {
return client.TaskListResult{Items: c.tasks}, c.err
}
+47 -37
View File
@@ -5,8 +5,8 @@ import (
"testing"
"time"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -46,15 +46,21 @@ func TestListTasks(t *testing.T) {
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.5"}),
taskStatus(taskState(swarmtypes.TaskStateFailed)),
),
swarmTask("id6",
taskSlot(6),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.6/24"}),
taskStatus(taskState(swarmtypes.TaskStateRunning)),
),
},
isGlobalSVC: false,
expectedTasks: []string{
"container.1",
"container.4",
"container.6",
},
networks: map[string]*networktypes.Summary{
"1": {
Name: "foo",
Network: networktypes.Network{Name: "foo"},
},
},
},
@@ -146,21 +152,23 @@ func TestSwarmProvider_listServices(t *testing.T) {
dockerVersion: "1.30",
networks: []networktypes.Summary{
{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.networktypes.driver.overlay.vxlanid_list": "4098",
"com.docker.networktypes.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
Network: networktypes.Network{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.networktypes.driver.overlay.vxlanid_list": "4098",
"com.docker.networktypes.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
},
},
},
},
@@ -201,21 +209,23 @@ func TestSwarmProvider_listServices(t *testing.T) {
dockerVersion: "1.30",
networks: []networktypes.Summary{
{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.networktypes.driver.overlay.vxlanid_list": "4098",
"com.docker.networktypes.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
Network: networktypes.Network{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.networktypes.driver.overlay.vxlanid_list": "4098",
"com.docker.networktypes.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
},
},
},
},
@@ -281,7 +291,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
},
networks: map[string]*networktypes.Summary{
"1": {
Name: "foo",
Network: networktypes.Network{Name: "foo"},
},
},
},
@@ -306,7 +316,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
},
networks: map[string]*networktypes.Summary{
"1": {
Name: "foo",
Network: networktypes.Network{Name: "foo"},
},
},
},
@@ -344,7 +354,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
},
networks: map[string]*networktypes.Summary{
"1": {
Name: "vlan",
Network: networktypes.Network{Name: "vlan"},
},
},
},
@@ -372,7 +382,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
},
networks: map[string]*networktypes.Summary{
"1": {
Name: "foo",
Network: networktypes.Network{Name: "vlan"},
},
},
},
+31 -34
View File
@@ -1,19 +1,21 @@
package docker
import (
"cmp"
"context"
"encoding/base64"
"fmt"
"net/http"
"slices"
"text/template"
"time"
"github.com/containerd/errdefs"
"github.com/docker/cli/cli/connhelper"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/docker/go-connections/sockets"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/rs/zerolog/log"
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/provider"
@@ -38,7 +40,7 @@ type Shared struct {
}
func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClient, containerID string) dockerData {
containerInspected, err := dockerClient.ContainerInspect(ctx, containerID)
res, err := dockerClient.ContainerInspect(ctx, containerID, client.ContainerInspectOptions{})
if err != nil {
if errdefs.IsNotFound(err) {
log.Ctx(ctx).Debug().Err(err).Msgf("Failed to inspect container %s", containerID)
@@ -50,8 +52,8 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie
// Always parse all containers (running and stopped)
// The allowNonRunning filtering will be applied later in service configuration
if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil {
return parseContainer(containerInspected)
if res.Container.State != nil {
return parseContainer(res.Container)
}
return dockerData{}
@@ -59,22 +61,19 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie
func parseContainer(container containertypes.InspectResponse) dockerData {
dData := dockerData{
ID: container.ID,
ServiceName: container.Name, // Default ServiceName to be the container's Name.
Name: container.Name,
Status: container.State.Status,
NetworkSettings: networkSettings{},
}
if container.ContainerJSONBase != nil {
dData.ID = container.ContainerJSONBase.ID
dData.Name = container.ContainerJSONBase.Name
dData.ServiceName = dData.Name // Default ServiceName to be the container's Name.
dData.Status = container.ContainerJSONBase.State.Status
if container.HostConfig != nil {
dData.NetworkSettings.NetworkMode = container.HostConfig.NetworkMode
}
if container.ContainerJSONBase.HostConfig != nil {
dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode
}
if container.State != nil && container.State.Health != nil {
dData.Health = container.State.Health.Status
}
if container.State != nil && container.State.Health != nil {
dData.Health = container.State.Health.Status
}
if container.Config != nil && container.Config.Labels != nil {
@@ -88,11 +87,12 @@ func parseContainer(container containertypes.InspectResponse) dockerData {
if container.NetworkSettings.Networks != nil {
dData.NetworkSettings.Networks = make(map[string]*networkData)
for name, containerNetwork := range container.NetworkSettings.Networks {
addr := containerNetwork.IPAddress
if addr == "" {
addr = containerNetwork.GlobalIPv6Address
var addr string
if containerNetwork.IPAddress.IsValid() {
addr = containerNetwork.IPAddress.String()
} else if containerNetwork.GlobalIPv6Address.IsValid() {
addr = containerNetwork.GlobalIPv6Address.String()
}
dData.NetworkSettings.Networks[name] = &networkData{
ID: containerNetwork.NetworkID,
Name: name,
@@ -127,10 +127,9 @@ func createClient(ctx context.Context, cfg ClientConfig) (*client.Client, error)
opts = append(opts,
client.FromEnv,
client.WithAPIVersionNegotiation(),
client.WithHTTPHeaders(httpHeaders))
return client.NewClientWithOpts(opts...)
return client.New(opts...)
}
func getClientOpts(ctx context.Context, cfg ClientConfig) ([]client.Opt, error) {
@@ -191,22 +190,20 @@ func getPort(container dockerData, serverPort string) string {
if len(serverPort) > 0 {
return serverPort
}
if len(container.NetworkSettings.Ports) == 0 {
return ""
}
var ports []nat.Port
var ports []networktypes.Port
for port := range container.NetworkSettings.Ports {
ports = append(ports, port)
}
less := func(i, j nat.Port) bool {
return i.Int() < j.Int()
}
nat.Sort(ports, less)
slices.SortFunc(ports, func(a, b networktypes.Port) int {
return cmp.Compare(a.Num(), b.Num())
})
if len(ports) > 0 {
return ports[0].Port()
}
return ""
return ports[0].Port()
}
func getServiceName(container dockerData) string {
+13 -14
View File
@@ -4,10 +4,9 @@ import (
"strconv"
"testing"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
containertypes "github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -26,16 +25,16 @@ func Test_getPort_docker(t *testing.T) {
},
{
desc: "binding, no server port label",
container: containerJSON(ports(nat.PortMap{
"80/tcp": {},
container: containerJSON(ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): {},
})),
expected: "80",
},
{
desc: "binding, multiple ports, no server port label",
container: containerJSON(ports(nat.PortMap{
"80/tcp": {},
"443/tcp": {},
container: containerJSON(ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): {},
networktypes.MustParsePort("443/tcp"): {},
})),
expected: "80",
},
@@ -48,17 +47,17 @@ func Test_getPort_docker(t *testing.T) {
{
desc: "binding, server port label",
container: containerJSON(
ports(nat.PortMap{
"80/tcp": {},
ports(networktypes.PortMap{
networktypes.MustParsePort("80/tcp"): {},
})),
serverPort: "8080",
expected: "8080",
},
{
desc: "binding, multiple ports, server port label",
container: containerJSON(ports(nat.PortMap{
"8080/tcp": {},
"80/tcp": {},
container: containerJSON(ports(networktypes.PortMap{
networktypes.MustParsePort("8080/tcp"): {},
networktypes.MustParsePort("80/tcp"): {},
})),
serverPort: "8080",
expected: "8080",
+21 -13
View File
@@ -1,15 +1,18 @@
package ecs
import (
"cmp"
"context"
"errors"
"fmt"
"math"
"net"
"slices"
"strconv"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/docker/go-connections/nat"
networktypes "github.com/moby/moby/api/types/network"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/config/label"
@@ -309,26 +312,31 @@ func getPort(instance ecsInstance, serverPort string) string {
return serverPort
}
var ports []nat.Port
var ports []networktypes.Port
for _, port := range instance.machine.ports {
natPort, err := nat.NewPort(string(port.protocol), strconv.FormatInt(int64(port.hostPort), 10))
if err != nil {
if port.hostPort < 0 || port.hostPort > math.MaxUint16 {
// port out of range
continue
}
if port.protocol == "" {
port.protocol = ecstypes.TransportProtocolTcp
}
natPort, ok := networktypes.PortFrom(uint16(port.hostPort), networktypes.IPProtocol(port.protocol))
if !ok {
continue
}
ports = append(ports, natPort)
}
less := func(i, j nat.Port) bool {
return i.Int() < j.Int()
}
nat.Sort(ports, less)
if len(ports) > 0 {
return ports[0].Port()
if len(ports) == 0 {
return ""
}
return ""
slices.SortFunc(ports, func(a, b networktypes.Port) int {
return cmp.Compare(a.Num(), b.Num())
})
return ports[0].Port()
}
func getServiceName(instance ecsInstance) string {
@@ -0,0 +1,29 @@
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: errorpage-wrr
namespace: default
spec:
weighted:
services:
- name: whoami
port: 80
weight: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: errorpage
namespace: default
spec:
errors:
status:
- "404"
- "500"
query: query
service:
name: errorpage-wrr
kind: TraefikService
+18 -16
View File
@@ -267,16 +267,20 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
continue
}
errorPage, errorPageService, err := p.createErrorPageMiddleware(ctxMid, client, middleware.Namespace, middleware.Spec.Errors)
errorPageName, errorPage, errorPageService, err := p.createErrorPageMiddleware(ctxMid, client, middleware.Namespace, middleware.Spec.Errors)
if err != nil {
logger.Error().Err(err).Msg("Error while reading error page middleware")
continue
}
if errorPage != nil && errorPageService != nil {
serviceName := id + "-errorpage-service"
errorPage.Service = serviceName
conf.HTTP.Services[serviceName] = errorPageService
if errorPage != nil {
if errorPageService != nil {
serviceName := id + "-errorpage-service"
errorPage.Service = serviceName
conf.HTTP.Services[serviceName] = errorPageService
} else {
errorPage.Service = errorPageName
}
}
plugin, err := createPluginMiddleware(client, middleware.Namespace, middleware.Spec.Plugin)
@@ -652,15 +656,9 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
return conf
}
func (p *Provider) createErrorPageMiddleware(ctx context.Context, client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
func (p *Provider) createErrorPageMiddleware(ctx context.Context, client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (string, *dynamic.ErrorPage, *dynamic.Service, error) {
if errorPage == nil {
return nil, nil, nil
}
errorPageMiddleware := &dynamic.ErrorPage{
Status: errorPage.Status,
StatusRewrites: errorPage.StatusRewrites,
Query: errorPage.Query,
return "", nil, nil, nil
}
cb := configBuilder{
@@ -670,12 +668,16 @@ func (p *Provider) createErrorPageMiddleware(ctx context.Context, client Client,
allowEmptyServices: p.AllowEmptyServices,
}
balancerServerHTTP, err := cb.buildServersLB(ctx, namespace, errorPage.Service.LoadBalancerSpec)
balancerName, balancerServerHTTP, err := cb.nameAndService(ctx, namespace, errorPage.Service.LoadBalancerSpec)
if err != nil {
return nil, nil, err
return "", nil, nil, err
}
return errorPageMiddleware, balancerServerHTTP, nil
return balancerName, &dynamic.ErrorPage{
Status: errorPage.Status,
StatusRewrites: errorPage.StatusRewrites,
Query: errorPage.Query,
}, balancerServerHTTP, nil
}
// getServicePort always returns a valid port, an error otherwise.
+24 -31
View File
@@ -389,7 +389,7 @@ func (c configBuilder) buildMirroring(ctx context.Context, tService *traefikv1al
}
// buildServersLB creates the configuration for the load-balancer of servers defined by svc.
func (c configBuilder) buildServersLB(ctx context.Context, namespace string, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
func (c configBuilder) buildServersLB(ctx context.Context, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()
@@ -403,7 +403,7 @@ func (c configBuilder) buildServersLB(ctx context.Context, namespace string, svc
// Here we are just logging a warning as the default value is already applied.
case "RoundRobin":
log.Warn().
Str("namespace", namespace).
Str("namespace", svc.Namespace).
Str("service", svc.Name).
Msgf("RoundRobin strategy value is deprecated, please use %s value instead", dynamic.BalancerStrategyWRR)
@@ -412,7 +412,7 @@ func (c configBuilder) buildServersLB(ctx context.Context, namespace string, svc
}
}
servers, err := c.loadServers(namespace, svc)
servers, err := c.loadServers(svc)
if err != nil {
return nil, err
}
@@ -507,14 +507,14 @@ func (c configBuilder) buildServersLB(ctx context.Context, namespace string, svc
}
}
lb.ServersTransport, err = c.makeServersTransportKey(namespace, svc.ServersTransport)
lb.ServersTransport, err = c.makeServersTransportKey(svc.Namespace, svc.ServersTransport)
if err != nil {
return nil, err
}
service := &dynamic.Service{LoadBalancer: lb}
if len(svc.Middlewares) > 0 {
mds, err := makeMiddlewareKeys(ctx, namespace, svc.Middlewares, c.allowCrossNamespace)
mds, err := makeMiddlewareKeys(ctx, svc.Namespace, svc.Middlewares, c.allowCrossNamespace)
if err != nil {
return nil, fmt.Errorf("could not create middleware keys: %w", err)
}
@@ -543,21 +543,13 @@ func (c configBuilder) makeServersTransportKey(parentNamespace string, serversTr
return provider.Normalize(makeID(parentNamespace, serversTransportName)), nil
}
func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
namespace := namespaceOrFallback(svc, parentNamespace)
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
return nil, fmt.Errorf("load balancer service %s/%s is not in the parent resource namespace %s", svc.Namespace, svc.Name, parentNamespace)
}
// If the service uses explicitly the provider suffix
sanitizedName := strings.TrimSuffix(svc.Name, providerNamespaceSeparator+ProviderName)
service, exists, err := c.client.GetService(namespace, sanitizedName)
func (c configBuilder) loadServers(svc traefikv1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
service, exists, err := c.client.GetService(svc.Namespace, svc.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("kubernetes service not found: %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("kubernetes service not found: %s/%s", svc.Namespace, svc.Name)
}
svcPort, err := getServicePort(service, svc.Port)
@@ -566,12 +558,12 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
}
if service.Spec.Type != corev1.ServiceTypeExternalName && svc.HealthCheck != nil {
return nil, fmt.Errorf("healthCheck allowed only for ExternalName services: %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("healthCheck allowed only for ExternalName services: %s/%s", svc.Namespace, svc.Name)
}
if service.Spec.Type == corev1.ServiceTypeExternalName {
if !c.allowExternalNameServices {
return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("externalName services not allowed: %s/%s", svc.Namespace, svc.Name)
}
protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port)
@@ -613,7 +605,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
return nil, nodesErr
}
if !nodesExists || len(nodes) == 0 {
return nil, fmt.Errorf("nodes not found for NodePort service %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("nodes not found for NodePort service %s/%s", svc.Namespace, svc.Name)
}
protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port)
@@ -634,13 +626,13 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
}
if len(servers) == 0 {
return nil, fmt.Errorf("no servers were generated for service %s in namespace", sanitizedName)
return nil, fmt.Errorf("no servers were generated for service %s in namespace", svc.Name)
}
return servers, nil
}
endpointSlices, err := c.client.GetEndpointSlicesForService(namespace, sanitizedName)
endpointSlices, err := c.client.GetEndpointSlicesForService(svc.Namespace, svc.Name)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
@@ -686,7 +678,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
}
if len(servers) == 0 && !c.allowEmptyServices {
return nil, fmt.Errorf("no servers found for %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("no servers found for %s/%s", svc.Namespace, svc.Name)
}
return servers, nil
@@ -699,25 +691,26 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
func (c configBuilder) nameAndService(ctx context.Context, parentNamespace string, service traefikv1alpha1.LoadBalancerSpec) (string, *dynamic.Service, error) {
svcCtx := log.Ctx(ctx).With().Str(logs.ServiceName, service.Name).Logger().WithContext(ctx)
namespace := namespaceOrFallback(service, parentNamespace)
service = *service.DeepCopy()
service.Namespace = namespaceOrFallback(service, parentNamespace)
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, service.Namespace) {
return "", nil, fmt.Errorf("service %s/%s not in the parent resource namespace %s", service.Namespace, service.Name, parentNamespace)
}
switch service.Kind {
case "", "Service":
serversLB, err := c.buildServersLB(ctx, namespace, service)
serversLB, err := c.buildServersLB(ctx, service)
if err != nil {
return "", nil, err
}
fullName := fullServiceName(svcCtx, namespace, service, service.Port)
fullName := fullServiceName(svcCtx, service, service.Port)
return fullName, serversLB, nil
case "TraefikService":
return fullServiceName(svcCtx, namespace, service, intstr.FromInt(0)), nil, nil
return fullServiceName(svcCtx, service, intstr.FromInt(0)), nil, nil
default:
return "", nil, fmt.Errorf("unsupported service kind %s", service.Kind)
@@ -797,18 +790,18 @@ func splitSvcNameProvider(name string) (string, string) {
return svc, pvd
}
func fullServiceName(ctx context.Context, namespace string, service traefikv1alpha1.LoadBalancerSpec, port intstr.IntOrString) string {
func fullServiceName(ctx context.Context, service traefikv1alpha1.LoadBalancerSpec, port intstr.IntOrString) string {
if (port.Type == intstr.Int && port.IntVal != 0) || (port.Type == intstr.String && port.StrVal != "") {
return provider.Normalize(fmt.Sprintf("%s-%s-%s", namespace, service.Name, &port))
return provider.Normalize(fmt.Sprintf("%s-%s-%s", service.Namespace, service.Name, &port))
}
if !strings.Contains(service.Name, providerNamespaceSeparator) {
return provider.Normalize(fmt.Sprintf("%s-%s", namespace, service.Name))
return provider.Normalize(fmt.Sprintf("%s-%s", service.Namespace, service.Name))
}
name, pName := splitSvcNameProvider(service.Name)
if pName == ProviderName {
return provider.Normalize(fmt.Sprintf("%s-%s", namespace, name))
return provider.Normalize(fmt.Sprintf("%s-%s", service.Namespace, name))
}
if service.Namespace != "" {
@@ -4974,6 +4974,65 @@ func TestLoadIngressRoutes(t *testing.T) {
},
},
},
{
desc: "Error page middleware referencing a TraefikService",
paths: []string{"services.yml", "with_error_page_traefik_service.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{
"default-errorpage": {
Errors: &dynamic.ErrorPage{
Status: []string{"404", "500"},
Service: "default-errorpage-wrr",
Query: "query",
},
},
},
Services: map[string]*dynamic.Service{
"default-errorpage-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-80",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"default-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
},
},
{
desc: "Simple Ingress Route, with options",
paths: []string{"services.yml", "with_options.yml"},
@@ -5394,7 +5394,7 @@ func TestLoadIngresses(t *testing.T) {
Headers: &dynamic.Headers{
AccessControlAllowCredentials: true,
AccessControlExposeHeaders: []string{},
AccessControlAllowHeaders: []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range,Authorization"},
AccessControlAllowHeaders: []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range", "Authorization"},
AccessControlAllowMethods: []string{"GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS"},
AccessControlAllowOriginList: []string{"*"},
AccessControlMaxAge: 1728000,
@@ -5404,7 +5404,7 @@ func TestLoadIngresses(t *testing.T) {
Headers: &dynamic.Headers{
AccessControlAllowCredentials: true,
AccessControlExposeHeaders: []string{},
AccessControlAllowHeaders: []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range,Authorization"},
AccessControlAllowHeaders: []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range", "Authorization"},
AccessControlAllowMethods: []string{"GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS"},
AccessControlAllowOriginList: []string{"*"},
AccessControlMaxAge: 1728000,
@@ -245,7 +245,7 @@ func (p *Provider) buildCORS(loc *location) {
loc.CORS = &dynamic.Headers{
AccessControlAllowCredentials: ptr.Deref(loc.Config.EnableCORSAllowCredentials, true),
AccessControlExposeHeaders: ptr.Deref(loc.Config.CORSExposeHeaders, []string{}),
AccessControlAllowHeaders: ptr.Deref(loc.Config.CORSAllowHeaders, []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range,Authorization"}),
AccessControlAllowHeaders: ptr.Deref(loc.Config.CORSAllowHeaders, []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range", "Authorization"}),
AccessControlAllowMethods: ptr.Deref(loc.Config.CORSAllowMethods, []string{"GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS"}),
AccessControlAllowOriginList: ptr.Deref(loc.Config.CORSAllowOrigin, []string{"*"}),
AccessControlMaxAge: int64(ptr.Deref(loc.Config.CORSMaxAge, 1728000)),
+3 -3
View File
@@ -4,11 +4,11 @@ RepositoryName = "traefik"
OutputType = "file"
FileName = "traefik_changelog.md"
# example new bugfix v3.6.15
# example new bugfix v3.6.16
CurrentRef = "v3.6"
PreviousRef = "v3.6.14"
PreviousRef = "v3.6.15"
BaseBranch = "v3.6"
FutureCurrentRefName = "v3.6.15"
FutureCurrentRefName = "v3.6.16"
ThresholdPreviousRef = 10000
ThresholdCurrentRef = 10000
+1
View File
@@ -0,0 +1 @@
yarn lint-staged
+8 -15
View File
@@ -18,16 +18,10 @@
},
"lint-staged": {
"**/*.{ts,tsx}": [
"yarn format",
"eslint --fix",
"git add"
"prettier --config .prettierrc.json --write",
"eslint --fix"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"browserslist": {
"production": [
">0.2%",
@@ -43,13 +37,13 @@
"type": "module",
"dependencies": {
"@eslint/js": "^9.32.0",
"@noble/ed25519": "^3.0.0",
"@noble/ed25519": "^3.0.1",
"@noble/hashes": "^2.0.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^14.5.2",
"@traefik-labs/faency": "12.0.7",
"@traefik-labs/faency": "12.3.3",
"@types/lodash": "^4.17.16",
"@types/node": "^22.15.18",
"@types/react": "^18.2.0",
@@ -81,7 +75,7 @@
"react-helmet-async": "^2.0.4",
"react-icons": "^5.0.1",
"react-infinite-scroll-hook": "^4.1.1",
"react-router-dom": "6.22.1",
"react-router-dom": "6.30.2",
"swr": "^2.2.4",
"typescript": "^5.2.2",
"typescript-eslint": "^8.38.0",
@@ -92,8 +86,8 @@
"vitest-canvas-mock": "^0.3.3"
},
"devDependencies": {
"husky": "^3.1.0",
"lint-staged": "^9.5.0",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"prettier": "^3.5.3"
},
"msw": {
@@ -104,7 +98,6 @@
"packageManager": "yarn@4.13.0",
"resolutions": {
"form-data": "4.0.4",
"lodash": "4.18.1",
"lodash-es": "4.18.1"
"js-yaml": "4.1.1"
}
}
+1239 -10906
View File
File diff suppressed because it is too large Load Diff