diff --git a/docs/content/migrate/v3.md b/docs/content/migrate/v3.md index ddc616a5d5..c1557e910a 100644 --- a/docs/content/migrate/v3.md +++ b/docs/content/migrate/v3.md @@ -9,6 +9,16 @@ This guide provides detailed migration steps for upgrading between different Tra --- +## 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. diff --git a/go.mod b/go.mod index 4b1aabfddc..5faab29113 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -50,6 +49,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. @@ -58,15 +59,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 @@ -194,7 +195,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 @@ -294,8 +295,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 @@ -303,7 +304,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 diff --git a/go.sum b/go.sum index 3dfa449858..78a4d7df92 100644 --- a/go.sum +++ b/go.sum @@ -920,8 +920,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= @@ -944,10 +944,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= @@ -958,8 +956,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= @@ -1627,14 +1625,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= @@ -1652,8 +1652,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= @@ -1874,8 +1872,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= @@ -1888,8 +1886,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= @@ -1975,10 +1973,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= @@ -1991,10 +1989,10 @@ github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso 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= @@ -2555,7 +2553,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= @@ -3113,6 +3110,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= diff --git a/integration/integration_test.go b/integration/integration_test.go index a68610d437..1cfcb14a5e 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -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 { diff --git a/pkg/provider/docker/builder_test.go b/pkg/provider/docker/builder_test.go index 70db961c85..817f7d7e0f 100644 --- a/pkg/provider/docker/builder_test.go +++ b/pkg/provider/docker/builder_test.go @@ -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) +} diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 96133db4fa..1a6d1de653 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -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 } diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index 33b8e390aa..b813688f66 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -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"}, }, }, }, diff --git a/pkg/provider/docker/data.go b/pkg/provider/docker/data.go index 22dde04e76..e9ace7b256 100644 --- a/pkg/provider/docker/data.go +++ b/pkg/provider/docker/data.go @@ -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 } diff --git a/pkg/provider/docker/pdocker.go b/pkg/provider/docker/pdocker.go index 937f6cdea0..438b839fbc 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -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" @@ -72,7 +70,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 @@ -93,12 +91,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) @@ -121,16 +113,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") } @@ -161,7 +155,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 { @@ -170,7 +164,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 diff --git a/pkg/provider/docker/pswarm.go b/pkg/provider/docker/pswarm.go index d16df07f86..d976581056 100644 --- a/pkg/provider/docker/pswarm.go +++ b/pkg/provider/docker/pswarm.go @@ -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" @@ -76,7 +74,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 @@ -157,17 +155,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") @@ -175,21 +173,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)) @@ -246,15 +246,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 } @@ -265,17 +264,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 } @@ -308,11 +305,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 { @@ -322,11 +319,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 } diff --git a/pkg/provider/docker/pswarm_mock_test.go b/pkg/provider/docker/pswarm_mock_test.go index d7af8f3120..b6a7f647b7 100644 --- a/pkg/provider/docker/pswarm_mock_test.go +++ b/pkg/provider/docker/pswarm_mock_test.go @@ -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 } diff --git a/pkg/provider/docker/pswarm_test.go b/pkg/provider/docker/pswarm_test.go index f762f884dc..0847303590 100644 --- a/pkg/provider/docker/pswarm_test.go +++ b/pkg/provider/docker/pswarm_test.go @@ -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"}, }, }, }, diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go index 537da208cb..4411118146 100644 --- a/pkg/provider/docker/shared.go +++ b/pkg/provider/docker/shared.go @@ -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 { diff --git a/pkg/provider/docker/shared_test.go b/pkg/provider/docker/shared_test.go index a5cf68defd..034d9f35cf 100644 --- a/pkg/provider/docker/shared_test.go +++ b/pkg/provider/docker/shared_test.go @@ -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", diff --git a/pkg/provider/ecs/config.go b/pkg/provider/ecs/config.go index 7e87f56753..3fda124a6d 100644 --- a/pkg/provider/ecs/config.go +++ b/pkg/provider/ecs/config.go @@ -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 {