From e1b9d6afa0b710ef57a55ea337421ae0aa82f245 Mon Sep 17 00:00:00 2001 From: Natalie Perret <11332444+natalie-o-perret@users.noreply.github.com> Date: Mon, 4 May 2026 12:54:48 +0200 Subject: [PATCH] EXOSCALE: migrate to egoscale v3 --- go.mod | 20 +- go.sum | 90 ++--- providers/exoscale/auditrecords.go | 25 +- providers/exoscale/exoscaleProvider.go | 380 ++++++++++---------- providers/exoscale/exoscaleProvider_test.go | 175 +++++++++ 5 files changed, 420 insertions(+), 270 deletions(-) create mode 100644 providers/exoscale/exoscaleProvider_test.go diff --git a/go.mod b/go.mod index 7324f6cef7..d5e3a47b98 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/DNSControl/dnscontrol/v4 -go 1.25.0 +go 1.26 retract v4.8.0 @@ -8,12 +8,26 @@ require ( github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/eclipse/paho.golang v0.23.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.9.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.3.0 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sony/gobreaker/v2 v2.4.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) require golang.org/x/net v0.53.0 @@ -38,7 +52,7 @@ require ( github.com/digitalocean/godo v1.188.0 github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c github.com/dnsimple/dnsimple-go/v8 v8.3.0 - github.com/exoscale/egoscale v0.102.4 + github.com/exoscale/egoscale/v3 v3.1.35 github.com/go-gandi/go-gandi v0.7.0 github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe github.com/gopherjs/jquery v0.0.0-20191017083323-73f4c7416038 @@ -115,7 +129,6 @@ require ( github.com/boombuler/barcode v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -124,7 +137,6 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.9.8 // indirect github.com/gofrs/flock v0.12.1 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/go-querystring v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect diff --git a/go.sum b/go.sum index be79d244af..da14b69673 100644 --- a/go.sum +++ b/go.sum @@ -114,15 +114,10 @@ github.com/cloudflare/cloudflare-go v0.116.0 h1:iRPMnTtnswRpELO65NTwMX4+RTdxZl+X github.com/cloudflare/cloudflare-go v0.116.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -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= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= -github.com/deepmap/oapi-codegen v1.9.1 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIxr1vtI= -github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw= github.com/digitalocean/godo v1.188.0 h1:QDxgjWnJYRf3JStAY9KM0xUqPC1YfXkPQWUMRRzraCk= github.com/digitalocean/godo v1.188.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354= @@ -136,8 +131,8 @@ github.com/eclipse/paho.golang v0.23.0/go.mod h1:nQRhTkoZv8EAiNs5UU0/WdQIx2NrnWU github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/exoscale/egoscale v0.102.4 h1:GBKsZMIOzwBfSu+4ZmWka3Ejf2JLiaBDHp4CQUgvp2E= -github.com/exoscale/egoscale v0.102.4/go.mod h1:ROSmPtle0wvf91iLZb09++N/9BH2Jo9XxIpAEumvocA= +github.com/exoscale/egoscale/v3 v3.1.35 h1:Rwwuh7wuwUKI09StObArGrx21ZR27FCPBjgISa1InTo= +github.com/exoscale/egoscale/v3 v3.1.35/go.mod h1:/1RTNibUdltIdzBbFxMMewNAkB6KKdxzRE/Icu8K5RU= github.com/failsafe-go/failsafe-go v0.9.6 h1:vPSH2cry0Ee5cnR9wc9qshCDO6jdrMA9elBJNwyo4Uk= github.com/failsafe-go/failsafe-go v0.9.6/go.mod h1:IeRpglkcwzKagjDMh90ZhN2l4Ovt3+jemQBUbThag54= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -151,11 +146,10 @@ github.com/fbiville/markdown-table-formatter v0.3.0/go.mod h1:q89TDtSEVDdTaufgSb github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-gandi/go-gandi v0.7.0 h1:gsP33dUspsN1M+ZW9HEgHchK9HiaSkYnltO73RHhSZA= github.com/go-gandi/go-gandi v0.7.0/go.mod h1:9NoYyfWCjFosClPiWjkbbRK5UViaZ4ctpT8/pKSSFlw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -164,10 +158,9 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -184,17 +177,13 @@ github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPE github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe h1:zn8tqiUbec4wR94o7Qj3LZCAT6uGobhEgnDRg6isG5U= github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.9.8 h1:5gMyLUeU1/6zl+WFfR1hN7D2kf+1/eRGa7DFtToiBvQ= github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -209,19 +198,15 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -244,7 +229,6 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gopherjs/jquery v0.0.0-20191017083323-73f4c7416038 h1:/gx6joY4PjXUu6mKM4yx7yj9Ti6yP8ljOxY/Qt0J25g= github.com/gopherjs/jquery v0.0.0-20191017083323-73f4c7416038/go.mod h1:xKR3tvLne+vYYPH9d4DM8X9MKlNV2yXDEomxulcK218= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= @@ -271,7 +255,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= @@ -296,26 +279,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= -github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= -github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= -github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= -github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/luadns/luadns-go v0.3.0 h1:mN2yhFv/LnGvPw/HmvYUhXe+lc95oXUqjlYVeJeOJng= github.com/luadns/luadns-go v0.3.0/go.mod h1:DmPXbrGMpynq1YNDpvgww3NP5Zf4wXM5raAbGrp5L+8= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -336,7 +306,6 @@ github.com/mittwald/go-powerdns v0.6.7/go.mod h1:zFe/i17IP6/NGFkWGGsPL0t7VrL6u14 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -361,6 +330,8 @@ github.com/oracle/oci-go-sdk/v65 v65.114.0 h1:7LvSJwXebrQ/iasML0ff1wcVN38rE0ZMzR github.com/oracle/oci-go-sdk/v65 v65.114.0/go.mod h1:oo33NDf2XPqx3/N6oLG4jFlrqJ0xu4Rlt9SfuAbtDFs= github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE= github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= +github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= +github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc= github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d h1:nf4+lHs8TQeIGFYZMcNg4iQOnZndLfYxnQaKEdqHVA4= @@ -393,6 +364,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= @@ -407,6 +380,14 @@ github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPc github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/sony/gobreaker/v2 v2.4.0 h1:g2KJRW1Ubty3+ZOcSEUN7K+REQJdN6yo6XvaML+jptg= github.com/sony/gobreaker/v2 v2.4.0/go.mod h1:pTyFJgcZ3h2tdQVLZZruK2C0eoFL1fb/G83wK1ZQl+s= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -414,7 +395,6 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -423,6 +403,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 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/transip/gotransip/v6 v6.27.1 h1:J8DfGAxnFZxNYdIRj59D6uFm0FPOkx9tF1aCkGgXeR8= @@ -431,14 +413,8 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI= github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vercel/terraform-provider-vercel v1.14.1 h1:ghAjFkMMzka4XuoBYdu1OXM/K7FQEj8wUd+xMPPOGrg= github.com/vercel/terraform-provider-vercel v1.14.1/go.mod h1:AdFCiUD0XP8XOi6tnhaCh7I0vyq2TAPmI+GcIp3+7SI= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -451,7 +427,6 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= @@ -481,12 +456,9 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= @@ -511,8 +483,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -526,11 +496,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -547,8 +514,6 @@ golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -561,10 +526,8 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -572,7 +535,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -587,7 +549,6 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -612,8 +573,6 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -625,8 +584,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= @@ -634,7 +591,6 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -670,9 +626,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -694,7 +647,6 @@ gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/providers/exoscale/auditrecords.go b/providers/exoscale/auditrecords.go index 0fd74ecf7a..0583762f81 100644 --- a/providers/exoscale/auditrecords.go +++ b/providers/exoscale/auditrecords.go @@ -1,23 +1,30 @@ package exoscale import ( + "fmt" + "github.com/DNSControl/dnscontrol/v4/models" "github.com/DNSControl/dnscontrol/v4/pkg/rejectif" ) -// AuditRecords returns a list of errors corresponding to the records -// that aren't supported by this provider. If all records are -// supported, an empty list is returned. +// AuditRecords returns a list of errors corresponding to the records that aren't supported by this provider. +// If all records are supported, an empty list is returned. func AuditRecords(records []*models.RecordConfig) []error { - a := rejectif.Auditor{} + auditor := rejectif.Auditor{} + + auditor.Add("CAA", rejectif.CaaTargetContainsWhitespace) // Last verified 2022-07-11 - a.Add("CAA", rejectif.CaaTargetContainsWhitespace) // Last verified 2022-07-11 + auditor.Add("MX", rejectif.MxNull) // Last verified 2022-07-11 - a.Add("MX", rejectif.MxNull) // Last verified 2022-07-11 + auditor.Add("PTR", func(rc *models.RecordConfig) error { + return fmt.Errorf("PTR records are not supported by the Exoscale provider") + }) - a.Add("SRV", rejectif.SrvHasNullTarget) // Last verified 2020-12-28 + auditor.Add("SRV", rejectif.SrvHasNullTarget) // Last verified 2020-12-28 - a.Add("TXT", rejectif.TxtHasUnpairedDoubleQuotes) // Last verified 2022-09-14 + auditor.Add("TXT", rejectif.TxtHasUnpairedBackslash) // Last verified 2026-05-04 + auditor.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2026-05-04 + auditor.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2026-05-04 - return a.Audit(records) + return auditor.Audit(records) } diff --git a/providers/exoscale/exoscaleProvider.go b/providers/exoscale/exoscaleProvider.go index d7712ba162..b44407d352 100644 --- a/providers/exoscale/exoscaleProvider.go +++ b/providers/exoscale/exoscaleProvider.go @@ -5,59 +5,58 @@ import ( "encoding/json" "errors" "fmt" + "os" "strconv" "strings" + egoscale "github.com/exoscale/egoscale/v3" + "github.com/exoscale/egoscale/v3/credentials" + "github.com/DNSControl/dnscontrol/v4/models" "github.com/DNSControl/dnscontrol/v4/pkg/diff" "github.com/DNSControl/dnscontrol/v4/pkg/printer" "github.com/DNSControl/dnscontrol/v4/pkg/providers" - egoscale "github.com/exoscale/egoscale/v2" -) - -const ( - defaultAPIZone = "ch-gva-2" ) -// ErrDomainNotFound error indicates domain name is not managed by Exoscale. -var ErrDomainNotFound = errors.New("domain not found") - type exoscaleProvider struct { - client *egoscale.Client - apiZone string + client *egoscale.Client } // NewExoscale creates a new Exoscale DNS provider. -func NewExoscale(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { - endpoint, apiKey, secretKey := m["dns-endpoint"], m["apikey"], m["secretkey"] - - client, err := egoscale.NewClient( - apiKey, - secretKey, - egoscale.ClientOptWithAPIEndpoint(endpoint), - ) +func NewExoscale(m map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) { + apiKey, secretKey := m["apikey"], m["secretkey"] + + creds := credentials.NewStaticCredentials(apiKey, secretKey) + client, err := egoscale.NewClient(creds) if err != nil { return nil, err } - provider := exoscaleProvider{ - client: client, - apiZone: defaultAPIZone, + // Endpoint is only for internal use now, not for production. + endpoint := os.Getenv("EXOSCALE_API_ENDPOINT") + if endpoint != "" { + client = client.WithEndpoint(egoscale.Endpoint(endpoint)) } - if z, ok := m["apizone"]; ok { - provider.apiZone = z + ctx := context.Background() + if zone, ok := m["apizone"]; ok { + endpoint, err := client.GetZoneAPIEndpoint(ctx, egoscale.ZoneName(zone)) + if err != nil { + return nil, fmt.Errorf("switch client zone: %w", err) + } + client = client.WithEndpoint(endpoint) } - return &provider, nil + return &exoscaleProvider{ + client: client, + }, nil } var features = providers.DocumentationNotes{ // The default for unlisted capabilities is 'Cannot'. // See providers/capabilities.go for the entire list of capabilities. - providers.CanConcur: providers.Unimplemented(), providers.CanGetZones: providers.Unimplemented(), - providers.CanOnlyDiff1Features: providers.Can(), + providers.CanConcur: providers.Cannot(), providers.CanUseAlias: providers.Can(), providers.CanUseCAA: providers.Can(), providers.CanUseLOC: providers.Cannot(), @@ -71,7 +70,7 @@ var features = providers.DocumentationNotes{ func init() { const providerName = "EXOSCALE" - const providerMaintainer = "@pierre-emmanuelJ" + const providerMaintainer = "@Giza" fns := providers.DspFuncs{ Initializer: NewExoscale, RecordAuditor: AuditRecords, @@ -81,149 +80,145 @@ func init() { } // EnsureZoneExists creates a zone if it does not exist. -func (c *exoscaleProvider) EnsureZoneExists(domain string, metadata map[string]string) error { - _, err := c.findDomainByName(domain) +func (provider *exoscaleProvider) EnsureZoneExists(domain string) error { + _, err := provider.findDomainByName(domain) + if errors.Is(err, egoscale.ErrNotFound) { + _, err = provider.client.CreateDNSDomain(context.Background(), egoscale.CreateDNSDomainRequest{ + UnicodeName: domain, + }) + } return err } // GetNameservers returns the nameservers for domain. -func (c *exoscaleProvider) GetNameservers(domain string) ([]*models.Nameserver, error) { +func (provider *exoscaleProvider) GetNameservers(_ string) ([]*models.Nameserver, error) { return nil, nil } // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. -func (c *exoscaleProvider) GetZoneRecords(dc *models.DomainConfig) (models.Records, error) { - domainName := dc.Name +func (provider *exoscaleProvider) GetZoneRecords(domainConfig *models.DomainConfig) (models.Records, error) { + domainName := domainConfig.Name - domain, err := c.findDomainByName(domainName) + domain, err := provider.findDomainByName(domainName) if err != nil { return nil, err } - domainID := *domain.ID + domainID := domain.ID ctx := context.Background() - records, err := c.client.ListDNSDomainRecords(ctx, c.apiZone, domainID) + records, err := provider.client.ListDNSDomainRecords(ctx, domainID) if err != nil { return nil, err } - existingRecords := make([]*models.RecordConfig, 0, len(records)) - for _, r := range records { - if r.ID == nil { - continue - } - - recordID := *r.ID - - record, err := c.client.GetDNSDomainRecord(ctx, c.apiZone, domainID, recordID) + existingRecords := make([]*models.RecordConfig, 0, len(records.DNSDomainRecords)) + for i := range records.DNSDomainRecords { + recordConfig, err := nativeToRecord(&records.DNSDomainRecords[i], domainName) if err != nil { return nil, err } - - // nil pointers are not expected, but just to be on the safe side... - var rtype, rcontent, rname string - if record.Type == nil { - continue - } - rtype = *record.Type - if record.Content != nil { - rcontent = *record.Content - } - if record.Name != nil { - rname = *record.Name + if recordConfig != nil { + existingRecords = append(existingRecords, recordConfig) } + } - if rtype == "SOA" || rtype == "NS" { - continue - } - if rname == "" { - t := "@" - record.Name = &t - } - if rtype == "CNAME" || rtype == "MX" || rtype == "ALIAS" || rtype == "SRV" { - t := rcontent + "." - // for SRV records we need to aditionally prefix target with priority, which API handles as separate field. - if rtype == "SRV" && record.Priority != nil { - t = fmt.Sprintf("%d %s", *record.Priority, t) - } - rcontent = t - } - // exoscale adds these odd txt records that mirror the alias records. - // they seem to manage them on deletes and things, so we'll just pretend they don't exist - if rtype == "TXT" && strings.HasPrefix(rcontent, "ALIAS for ") { - continue - } + return existingRecords, nil +} - rc := &models.RecordConfig{ - Original: record, - } - if record.TTL != nil { - rc.TTL = uint32(*record.TTL) - } - rc.SetLabel(rname, domainName) - - switch rtype { - case "ALIAS", "URL": - rc.Type = rtype - err = rc.SetTarget(rcontent) - case "MX": - var prio uint16 - if record.Priority != nil { - prio = uint16(*record.Priority) - } - err = rc.SetTargetMX(prio, rcontent) - default: - err = rc.PopulateFromString(rtype, rcontent, domainName) +// nativeToRecord converts an Exoscale DNS record to a RecordConfig. +// Returns nil, nil for record types that should be silently skipped (SOA, NS, TXT ALIAS mirrors). +func nativeToRecord(record *egoscale.DNSDomainRecord, domainName string) (*models.RecordConfig, error) { + recordContent := record.Content + + if record.Type == "SOA" || record.Type == "NS" { + return nil, nil + } + if record.Name == "" { + record.Name = "@" + } + if record.Type == "CNAME" || record.Type == "MX" || record.Type == "ALIAS" || record.Type == "SRV" { + if !strings.HasSuffix(recordContent, ".") { + recordContent += "." } - if err != nil { - return nil, fmt.Errorf("unparsable record received from exoscale: %w", err) + // for SRV records we need to additionally prefix target with priority, which API handles as separate field. + if record.Type == "SRV" && record.Priority != 0 { + recordContent = fmt.Sprintf("%d %s", record.Priority, recordContent) } + } + // Based on tests, exoscale adds these odd txt records that mirror the alias records. + if record.Type == "TXT" && strings.HasPrefix(recordContent, "ALIAS for ") { + return nil, nil + } - existingRecords = append(existingRecords, rc) + recordConfig := &models.RecordConfig{ + Original: record, + } + if record.Ttl != 0 { + recordConfig.TTL = uint32(record.Ttl) + } + recordConfig.SetLabel(record.Name, domainName) + + var err error + switch record.Type { + case "ALIAS", "URL": + recordConfig.Type = string(record.Type) + _ = recordConfig.SetTarget(recordContent) + case "MX": + var priority uint16 + if record.Priority != 0 { + priority = uint16(record.Priority) + } + err = recordConfig.SetTargetMX(priority, recordContent) + default: + err = recordConfig.PopulateFromString(string(record.Type), recordContent, domainName) + } + if err != nil { + return nil, fmt.Errorf("unparsable record received from exoscale: %w", err) } - return existingRecords, nil + return recordConfig, nil } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { - removeOtherNS(dc) - domain, err := c.findDomainByName(dc.Name) +func (provider *exoscaleProvider) GetZoneRecordsCorrections( + domainConfig *models.DomainConfig, + existingRecords models.Records) ([]*models.Correction, int, error) { + removeOtherNS(domainConfig) + domain, err := provider.findDomainByName(domainConfig.Name) if err != nil { return nil, 0, err } - domainID := *domain.ID - toReport, create, toDelete, modify, actualChangeCount, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, toCreate, toDelete, toUpdate, actualChangeCount, err := diff.NewCompat(domainConfig).IncrementalDiff(existingRecords) if err != nil { return nil, 0, err } // Start corrections with the reports corrections := diff.GenerateMessageCorrections(toReport) - for _, del := range toDelete { - record := del.Existing.Original.(*egoscale.DNSDomainRecord) + for _, deletionCorrelation := range toDelete { + record := deletionCorrelation.Existing.Original.(*egoscale.DNSDomainRecord) corrections = append(corrections, &models.Correction{ - Msg: del.String(), - F: c.deleteRecordFunc(*record.ID, domainID), + Msg: deletionCorrelation.String(), + F: provider.deleteRecordFunc(record.ID, domain.ID), }) } - for _, cre := range create { - rc := cre.Desired + for _, creationCorrelation := range toCreate { + recordConfig := creationCorrelation.Desired corrections = append(corrections, &models.Correction{ - Msg: cre.String(), - F: c.createRecordFunc(rc, domainID), + Msg: creationCorrelation.String(), + F: provider.createRecordFunc(recordConfig, domain.ID), }) } - for _, mod := range modify { - oldRec := mod.Existing.Original.(*egoscale.DNSDomainRecord) - newRec := mod.Desired + for _, updateCorrelation := range toUpdate { + old := updateCorrelation.Existing.Original.(*egoscale.DNSDomainRecord) + new := updateCorrelation.Desired corrections = append(corrections, &models.Correction{ - Msg: mod.String(), - F: c.updateRecordFunc(oldRec, newRec, domainID), + Msg: updateCorrelation.String(), + F: provider.updateRecordFunc(old, new, domain.ID), }) } @@ -231,22 +226,23 @@ func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex } // Returns a function that can be invoked to create a record in a zone. -func (c *exoscaleProvider) createRecordFunc(rc *models.RecordConfig, domainID string) func() error { +func (provider *exoscaleProvider) createRecordFunc( + recordConfig *models.RecordConfig, + domainID egoscale.UUID) func() error { return func() error { - target := rc.GetTargetCombined() - name := rc.GetLabel() - var prio *int64 + target := recordConfig.GetTargetCombined() + name := recordConfig.GetLabel() + var prio int64 - if rc.Type == "MX" { - target = rc.GetTargetField() + if recordConfig.Type == "MX" { + target = recordConfig.GetTargetField() - if rc.MxPreference != 0 { - p := int64(rc.MxPreference) - prio = &p + if recordConfig.MxPreference != 0 { + prio = int64(recordConfig.MxPreference) } } - if rc.Type == "SRV" { + if recordConfig.Type == "SRV" { // API wants priority as a separate argument, here we will strip it from combined target. sp := strings.Split(target, " ") target = strings.Join(sp[1:], " ") @@ -254,59 +250,68 @@ func (c *exoscaleProvider) createRecordFunc(rc *models.RecordConfig, domainID st if err != nil { return err } - prio = &p + prio = p } - if rc.Type == "NS" && (name == "@" || name == "") { + if recordConfig.Type == "NS" && (name == "@" || name == "") { name = "*" } - record := egoscale.DNSDomainRecord{ - Name: &name, - Type: &rc.Type, - Content: &target, + record := egoscale.CreateDNSDomainRecordRequest{ + Name: name, + Type: egoscale.CreateDNSDomainRecordRequestType(recordConfig.Type), + Content: target, Priority: prio, } - if rc.TTL != 0 { - ttl := int64(rc.TTL) - record.TTL = &ttl + if recordConfig.TTL != 0 { + record.Ttl = int64(recordConfig.TTL) } - _, err := c.client.CreateDNSDomainRecord(context.Background(), c.apiZone, domainID, &record) + ctx := context.Background() + op, err := provider.client.CreateDNSDomainRecord(ctx, domainID, record) + if err != nil { + return err + + } + _, err = provider.client.Wait(ctx, op, egoscale.OperationStateSuccess) return err } } // Returns a function that can be invoked to delete a record in a zone. -func (c *exoscaleProvider) deleteRecordFunc(recordID, domainID string) func() error { +func (provider *exoscaleProvider) deleteRecordFunc(recordID, domainID egoscale.UUID) func() error { return func() error { - return c.client.DeleteDNSDomainRecord( - context.Background(), - c.apiZone, - domainID, - &egoscale.DNSDomainRecord{ID: &recordID}, - ) + ctx := context.Background() + op, err := provider.client.DeleteDNSDomainRecord(ctx, domainID, recordID) + if err != nil { + return err + } + + _, err = provider.client.Wait(ctx, op, egoscale.OperationStateSuccess) + return err } } // Returns a function that can be invoked to update a record in a zone. -func (c *exoscaleProvider) updateRecordFunc(record *egoscale.DNSDomainRecord, rc *models.RecordConfig, domainID string) func() error { +func (provider *exoscaleProvider) updateRecordFunc( + record *egoscale.DNSDomainRecord, + recordConfig *models.RecordConfig, + domainID egoscale.UUID) func() error { return func() error { - target := rc.GetTargetCombined() - name := rc.GetLabel() + target := recordConfig.GetTargetCombined() + name := recordConfig.GetLabel() - if rc.Type == "MX" { - target = rc.GetTargetField() + if recordConfig.Type == "MX" { + target = recordConfig.GetTargetField() - if rc.MxPreference != 0 { - p := int64(rc.MxPreference) - record.Priority = &p + if recordConfig.MxPreference != 0 { + record.Priority = int64(recordConfig.MxPreference) } } - if rc.Type == "SRV" { + if recordConfig.Type == "SRV" { // API wants priority as separate argument, here we will strip it from combined target. sp := strings.Split(target, " ") target = strings.Join(sp[1:], " ") @@ -314,66 +319,65 @@ func (c *exoscaleProvider) updateRecordFunc(record *egoscale.DNSDomainRecord, rc if err != nil { return err } - record.Priority = &p + record.Priority = p } - if rc.Type == "NS" && (name == "@" || name == "") { + if recordConfig.Type == "NS" && (name == "@" || name == "") { name = "*" } - record.Name = &name - record.Type = &rc.Type - record.Content = &target - if rc.TTL != 0 { - ttl := int64(rc.TTL) - record.TTL = &ttl + record.Name = name + record.Type = egoscale.DNSDomainRecordType(recordConfig.Type) + record.Content = target + if recordConfig.TTL != 0 { + record.Ttl = int64(recordConfig.TTL) } - return c.client.UpdateDNSDomainRecord( - context.Background(), - c.apiZone, - domainID, - record, - ) + ctx := context.Background() + op, err := provider.client.UpdateDNSDomainRecord(ctx, domainID, record.ID, egoscale.UpdateDNSDomainRecordRequest{ + Name: record.Name, + Content: record.Content, + Priority: record.Priority, + Ttl: record.Ttl, + }) + if err != nil { + return err + } + _, err = provider.client.Wait(ctx, op, egoscale.OperationStateSuccess) + return err } } -func (c *exoscaleProvider) findDomainByName(name string) (*egoscale.DNSDomain, error) { - domains, err := c.client.ListDNSDomains(context.Background(), c.apiZone) +func (provider *exoscaleProvider) findDomainByName(name string) (egoscale.DNSDomain, error) { + domains, err := provider.client.ListDNSDomains(context.Background()) if err != nil { - return nil, err - } - - for _, domain := range domains { - if domain.UnicodeName != nil && domain.ID != nil && *domain.UnicodeName == name { - return &domain, nil - } + return egoscale.DNSDomain{}, err } - return nil, ErrDomainNotFound + return domains.FindDNSDomain(name) } func defaultNSSUffix(defNS string) bool { - return (strings.HasSuffix(defNS, ".exoscale.io.") || + return strings.HasSuffix(defNS, ".exoscale.io.") || strings.HasSuffix(defNS, ".exoscale.com.") || strings.HasSuffix(defNS, ".exoscale.ch.") || - strings.HasSuffix(defNS, ".exoscale.net.")) + strings.HasSuffix(defNS, ".exoscale.net.") } // remove all non-exoscale NS records from our desired state. // if any are found, print a warning. -func removeOtherNS(dc *models.DomainConfig) { - newList := make([]*models.RecordConfig, 0, len(dc.Records)) - for _, rec := range dc.Records { - if rec.Type == "NS" { +func removeOtherNS(domainConfig *models.DomainConfig) { + recordConfigs := make([]*models.RecordConfig, 0, len(domainConfig.Records)) + for _, recordConfig := range domainConfig.Records { + if recordConfig.Type == "NS" { // apex NS inside exoscale are expected. - if rec.GetLabelFQDN() == dc.Name && defaultNSSUffix(rec.GetTargetField()) { + if recordConfig.GetLabelFQDN() == domainConfig.Name && defaultNSSUffix(recordConfig.GetTargetField()) { continue } - printer.Printf("Warning: exoscale.com(.io, .ch, .net) does not allow NS records to be modified. %s will not be added.\n", rec.GetTargetField()) + printer.Printf("Warning: exoscale.com(.io, .ch, .net) does not allow NS records to be modified. %s will not be added.\n", recordConfig.GetTargetField()) continue } - newList = append(newList, rec) + recordConfigs = append(recordConfigs, recordConfig) } - dc.Records = newList + domainConfig.Records = recordConfigs } diff --git a/providers/exoscale/exoscaleProvider_test.go b/providers/exoscale/exoscaleProvider_test.go new file mode 100644 index 0000000000..e51fe2dfe8 --- /dev/null +++ b/providers/exoscale/exoscaleProvider_test.go @@ -0,0 +1,175 @@ +package exoscale + +import ( + "testing" + + egoscale "github.com/exoscale/egoscale/v3" + + "github.com/DNSControl/dnscontrol/v4/models" +) + +func makeNativeRecord(rtype, name, content string, priority int64, ttl int64) *egoscale.DNSDomainRecord { + return &egoscale.DNSDomainRecord{ + Type: egoscale.DNSDomainRecordType(rtype), + Name: name, + Content: content, + Priority: priority, + Ttl: ttl, + } +} + +// TestNativeToRecord_ContentPopulated regression: rcontent was declared as "" and never +// assigned from record.Content, so all records got empty content. +func TestNativeToRecord_ContentPopulated(t *testing.T) { + tests := []struct { + name string + record *egoscale.DNSDomainRecord + wantTarget string + wantType string + }{ + { + name: "A", + record: makeNativeRecord("A", "foo", "1.2.3.4", 0, 300), + wantTarget: "1.2.3.4", + wantType: "A", + }, + { + name: "AAAA", + record: makeNativeRecord("AAAA", "foo", "2001:db8::1", 0, 300), + wantTarget: "2001:db8::1", + wantType: "AAAA", + }, + { + name: "TXT", + record: makeNativeRecord("TXT", "foo", "hello world", 0, 300), + wantTarget: "hello world", + wantType: "TXT", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rc, err := nativeToRecord(tt.record, "example.com") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rc == nil { + t.Fatal("expected record, got nil") + } + if rc.Type != tt.wantType { + t.Errorf("type: got %q, want %q", rc.Type, tt.wantType) + } + if rc.GetTargetField() != tt.wantTarget { + t.Errorf("target: got %q, want %q", rc.GetTargetField(), tt.wantTarget) + } + }) + } +} + +// TestNativeToRecord_SRVNoDoubleDot: API returns content as "weight port target"; we appended +// a trailing dot unconditionally, causing double-dot when the API already returned one. +func TestNativeToRecord_SRVNoDoubleDot(t *testing.T) { + tests := []struct { + name string + content string // as returned by the API: "weight port target" + }{ + {"no trailing dot", "20 5060 foo.example.com"}, + {"trailing dot already present", "20 5060 foo.example.com."}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + record := makeNativeRecord("SRV", "_sip._tcp", tt.content, 10, 300) + rc, err := nativeToRecord(record, "example.com") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rc == nil { + t.Fatal("expected record, got nil") + } + // target should end with exactly one dot + target := rc.GetTargetField() + if len(target) >= 2 && target[len(target)-1] == '.' && target[len(target)-2] == '.' { + t.Errorf("double trailing dot in SRV target: %q", target) + } + }) + } +} + +// TestNativeToRecord_CNAMENoDoubleDot: same trailing-dot guard for CNAME. +func TestNativeToRecord_CNAMENoDoubleDot(t *testing.T) { + for _, content := range []string{"target.example.com", "target.example.com."} { + record := makeNativeRecord("CNAME", "foo", content, 0, 300) + rc, err := nativeToRecord(record, "example.com") + if err != nil { + t.Fatalf("unexpected error for content %q: %v", content, err) + } + target := rc.GetTargetField() + if len(target) >= 2 && target[len(target)-1] == '.' && target[len(target)-2] == '.' { + t.Errorf("double trailing dot in CNAME target for input %q: got %q", content, target) + } + } +} + +// TestNativeToRecord_SkippedTypes: SOA, NS, and TXT ALIAS mirrors should return nil. +func TestNativeToRecord_SkippedTypes(t *testing.T) { + skipped := []*egoscale.DNSDomainRecord{ + makeNativeRecord("SOA", "@", "ns1.example.com", 0, 3600), + makeNativeRecord("NS", "@", "ns1.example.com.", 0, 3600), + makeNativeRecord("TXT", "foo", "ALIAS for bar.example.com", 0, 300), + } + + for _, r := range skipped { + rc, err := nativeToRecord(r, "example.com") + if err != nil { + t.Errorf("type %s: unexpected error: %v", r.Type, err) + } + if rc != nil { + t.Errorf("type %s: expected nil, got record", r.Type) + } + } +} + +// TestAuditRecords_PTRRejected: API does not support PTR; provider must reject it. +func TestAuditRecords_PTRRejected(t *testing.T) { + rc := &models.RecordConfig{Type: "PTR"} + rc.SetLabel("4", "example.com") + rc.MustSetTarget("foo.example.com.") + + errs := AuditRecords([]*models.RecordConfig{rc}) + if len(errs) == 0 { + t.Error("expected PTR to be rejected, got no errors") + } +} + +// TestAuditRecords_EmptyTXTRejected: API rejects empty TXT values. +func TestAuditRecords_EmptyTXTRejected(t *testing.T) { + rc := &models.RecordConfig{Type: "TXT"} + rc.SetLabel("foo", "example.com") + _ = rc.SetTargetTXT("") + + errs := AuditRecords([]*models.RecordConfig{rc}) + if len(errs) == 0 { + t.Error("expected empty TXT to be rejected, got no errors") + } +} + +// TestAuditRecords_ValidRecordsPass: common record types should pass without errors. +func TestAuditRecords_ValidRecordsPass(t *testing.T) { + var records []*models.RecordConfig + + a := &models.RecordConfig{Type: "A"} + a.SetLabel("foo", "example.com") + a.MustSetTarget("1.2.3.4") + records = append(records, a) + + txt := &models.RecordConfig{Type: "TXT"} + txt.SetLabel("foo", "example.com") + _ = txt.SetTargetTXT("v=spf1 -all") + records = append(records, txt) + + errs := AuditRecords(records) + if len(errs) != 0 { + t.Errorf("expected no errors, got: %v", errs) + } +}