diff --git a/docs/flags.md b/docs/flags.md index b3da164121..7f3e54d53d 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -136,6 +136,7 @@ tags: | `--exoscale-apizone="ch-gva-2"` | When using Exoscale provider, specify the API Zone (optional) | | `--exoscale-apikey=""` | Provide your API Key for the Exoscale provider | | `--exoscale-apisecret=""` | Provide your API Secret for the Exoscale provider | +| `--exoscale-zones-cache-duration=0s` | When using Exoscale provider, set the zones list cache TTL (0s to disable) | | `--rfc2136-host=` | When using the RFC2136 provider, specify the host of the DNS server (optionally specify multiple times when using --rfc2136-load-balancing-strategy) | | `--rfc2136-port=0` | When using the RFC2136 provider, specify the port of the DNS server | | `--rfc2136-zone=RFC2136-ZONE` | When using the RFC2136 provider, specify zone entry of the DNS server to use (can be specified multiple times) | diff --git a/go.mod b/go.mod index 1546e7c52e..33c457b207 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/datawire/ambassador v1.12.4 github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace github.com/dnsimple/dnsimple-go v1.7.0 - github.com/exoscale/egoscale v0.102.3 + github.com/exoscale/egoscale/v3 v3.1.34 github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 github.com/go-gandi/go-gandi v0.7.0 github.com/go-logr/logr v1.4.3 @@ -100,10 +100,10 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.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/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.22.4 // indirect @@ -120,9 +120,12 @@ require ( github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // 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/go-resty/resty/v2 v2.17.2 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gofrs/flock v0.10.0 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -149,6 +152,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -157,17 +161,23 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterhellberg/link v1.1.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/schollz/progressbar/v3 v3.8.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sosodev/duration v1.3.1 // 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/stretchr/objx v0.5.3 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/go.sum b/go.sum index 46120bcbd2..1e240c488b 100644 --- a/go.sum +++ b/go.sum @@ -222,7 +222,6 @@ 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/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/datawire/ambassador v1.12.4 h1:g+agFHayLqETkCgFgEQi9qk4zakE0UAhgK8xVUEcDDI= github.com/datawire/ambassador v1.12.4/go.mod h1:2grBLdYgILzrgTpenDMB5OeyhObIUaT+KwkLkZI1KDE= @@ -233,10 +232,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -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/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= @@ -286,8 +281,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/exoscale/egoscale v0.102.3 h1:DYqN2ipoLKpiFoprRGQkp2av/Ze7sUYYlGhi1N62tfY= -github.com/exoscale/egoscale v0.102.3/go.mod h1:RPf2Gah6up+6kAEayHTQwqapzXlm93f0VQas/UEGU5c= +github.com/exoscale/egoscale/v3 v3.1.34 h1:POTsCy3IhSR9L8ZN89hiKlfeQ9L+D7NncoKb10WsyaA= +github.com/exoscale/egoscale/v3 v3.1.34/go.mod h1:0iY8OxgHJCS5TKqDNhwOW95JBKCnBZl3YGU4Yt+NqkU= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -300,6 +295,8 @@ github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99/go.mod h1:4mP9w github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -307,14 +304,10 @@ github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8 github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= 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= @@ -348,7 +341,6 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= @@ -413,12 +405,13 @@ github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16p github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +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= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk= github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= @@ -427,6 +420,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= @@ -434,7 +429,6 @@ github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8z github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -443,8 +437,6 @@ github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVH github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.10.0 h1:SHMXenfaB03KbroETaCMtbBg3Yn29v4w1r+tgy4ff4k= github.com/gofrs/flock v0.10.0/go.mod h1:FirDy1Ing0mI2+kB6wk+vyyAH+e6xiE+EYA0jnzV9jc= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -453,7 +445,6 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -482,7 +473,6 @@ 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.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= @@ -536,7 +526,6 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= @@ -670,19 +659,10 @@ 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/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -704,16 +684,13 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.11/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= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -837,6 +814,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterhellberg/link v1.1.0 h1:s2+RH8EGuI/mI4QwrWGSYQCRz7uNgip9BaM04HKu5kc= @@ -919,6 +898,8 @@ github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= @@ -954,8 +935,12 @@ github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERA github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -970,6 +955,8 @@ 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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +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/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -994,6 +981,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -1015,17 +1004,11 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMW 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.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -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 v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -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/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -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/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4= github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= @@ -1123,10 +1106,7 @@ golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= @@ -1152,7 +1132,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= @@ -1183,13 +1162,11 @@ golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -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-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= @@ -1205,7 +1182,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= @@ -1239,11 +1215,9 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1254,14 +1228,11 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-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-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -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.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= @@ -1279,8 +1250,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1310,9 +1279,7 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= @@ -1376,7 +1343,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi 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/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index e6b837785d..3ad530749c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -179,6 +179,7 @@ type Config struct { ExoscaleAPISecret string `secure:"yes"` ExoscaleAPIEnvironment string ExoscaleAPIZone string + ExoscaleZoneCacheDuration time.Duration CRDSourceAPIVersion string CRDSourceKind string ServiceTypeFilter []string @@ -294,6 +295,7 @@ var defaultConfig = &Config{ ExoscaleAPIKey: "", ExoscaleAPISecret: "", ExoscaleAPIZone: "ch-gva-2", + ExoscaleZoneCacheDuration: 0 * time.Second, ExposeInternalIPV6: false, FQDNTemplate: "", TargetTemplate: "", @@ -665,6 +667,7 @@ func bindFlags(b flags.FlagBinder, cfg *Config) { b.StringVar("exoscale-apizone", "When using Exoscale provider, specify the API Zone (optional)", defaultConfig.ExoscaleAPIZone, &cfg.ExoscaleAPIZone) b.StringVar("exoscale-apikey", "Provide your API Key for the Exoscale provider", defaultConfig.ExoscaleAPIKey, &cfg.ExoscaleAPIKey) b.StringVar("exoscale-apisecret", "Provide your API Secret for the Exoscale provider", defaultConfig.ExoscaleAPISecret, &cfg.ExoscaleAPISecret) + b.DurationVar("exoscale-zones-cache-duration", "When using Exoscale provider, set the zones list cache TTL (0s to disable)", defaultConfig.ExoscaleZoneCacheDuration, &cfg.ExoscaleZoneCacheDuration) // Flags related to RFC2136 provider b.StringsVar("rfc2136-host", "When using the RFC2136 provider, specify the host of the DNS server (optionally specify multiple times when using --rfc2136-load-balancing-strategy)", []string{defaultConfig.RFC2136Host[0]}, &cfg.RFC2136Host) diff --git a/provider/exoscale/exoscale.go b/provider/exoscale/exoscale.go index 853c4306c9..24e2b1f406 100644 --- a/provider/exoscale/exoscale.go +++ b/provider/exoscale/exoscale.go @@ -18,25 +18,76 @@ package exoscale import ( "context" + "fmt" "strings" + "time" - egoscale "github.com/exoscale/egoscale/v2" - exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" + "github.com/exoscale/egoscale/v3/credentials" log "github.com/sirupsen/logrus" "sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/pkg/apis/externaldns" "sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/provider" + "sigs.k8s.io/external-dns/provider/blueprint" ) // EgoscaleClientI for replaceable implementation type EgoscaleClientI interface { - ListDNSDomainRecords(context.Context, string, string) ([]egoscale.DNSDomainRecord, error) - ListDNSDomains(context.Context, string) ([]egoscale.DNSDomain, error) - CreateDNSDomainRecord(context.Context, string, string, *egoscale.DNSDomainRecord) (*egoscale.DNSDomainRecord, error) - DeleteDNSDomainRecord(context.Context, string, string, *egoscale.DNSDomainRecord) error - UpdateDNSDomainRecord(context.Context, string, string, *egoscale.DNSDomainRecord) error + ListDNSDomains(context.Context) ([]v3.DNSDomain, error) + ListDNSDomainRecords(context.Context, v3.UUID) ([]v3.DNSDomainRecord, error) + CreateDNSDomainRecord(context.Context, v3.UUID, v3.CreateDNSDomainRecordRequest) error + DeleteDNSDomainRecord(context.Context, v3.UUID, v3.UUID) error + UpdateDNSDomainRecord(context.Context, v3.UUID, v3.UUID, v3.UpdateDNSDomainRecordRequest) error +} + +// exoscaleClient wraps the real v3 client to satisfy EgoscaleClientI. +type exoscaleClient struct { + c *v3.Client +} + +func (w *exoscaleClient) ListDNSDomains(ctx context.Context) ([]v3.DNSDomain, error) { + resp, err := w.c.ListDNSDomains(ctx) + if err != nil { + return nil, err + } + return resp.DNSDomains, nil +} + +func (w *exoscaleClient) ListDNSDomainRecords(ctx context.Context, domainID v3.UUID) ([]v3.DNSDomainRecord, error) { + resp, err := w.c.ListDNSDomainRecords(ctx, domainID) + if err != nil { + return nil, err + } + return resp.DNSDomainRecords, nil +} + +func (w *exoscaleClient) CreateDNSDomainRecord(ctx context.Context, domainID v3.UUID, req v3.CreateDNSDomainRecordRequest) error { + op, err := w.c.CreateDNSDomainRecord(ctx, domainID, req) + if err != nil { + return err + } + _, err = w.c.Wait(ctx, op, v3.OperationStateSuccess) + return err +} + +func (w *exoscaleClient) DeleteDNSDomainRecord(ctx context.Context, domainID v3.UUID, recordID v3.UUID) error { + op, err := w.c.DeleteDNSDomainRecord(ctx, domainID, recordID) + if err != nil { + return err + } + _, err = w.c.Wait(ctx, op, v3.OperationStateSuccess) + return err +} + +func (w *exoscaleClient) UpdateDNSDomainRecord(ctx context.Context, domainID v3.UUID, recordID v3.UUID, req v3.UpdateDNSDomainRecordRequest) error { + op, err := w.c.UpdateDNSDomainRecord(ctx, domainID, recordID, req) + if err != nil { + return err + } + _, err = w.c.Wait(ctx, op, v3.OperationStateSuccess) + return err } // ExoscaleProvider initialized as dns provider with no records @@ -44,9 +95,8 @@ type ExoscaleProvider struct { provider.BaseProvider domain *endpoint.DomainFilter client EgoscaleClientI - apiEnv string - apiZone string filter *zoneFilter + zoneCache *blueprint.ZoneCache[map[string]string] OnApplyChanges func(changes *plan.Changes) dryRun bool } @@ -62,33 +112,36 @@ func New(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.Doma cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, + cfg.ExoscaleZoneCacheDuration, ExoscaleWithDomain(domainFilter), ExoscaleWithLogging(), ) } // newProvider returns ExoscaleProvider DNS provider interface implementation -func newProvider(env, zone, key, secret string, dryRun bool, opts ...ExoscaleOption) (*ExoscaleProvider, error) { - client, err := egoscale.NewClient( - key, - secret, - ) +func newProvider(env, zone, key, secret string, dryRun bool, zoneCacheDuration time.Duration, opts ...ExoscaleOption) (*ExoscaleProvider, error) { + creds := credentials.NewStaticCredentials(key, secret) + + // Build endpoint from env and zone, e.g. env="api", zone="ch-gva-2" + // yields "https://api-ch-gva-2.exoscale.com/v2" + endpoint := v3.Endpoint(fmt.Sprintf("https://%s-%s.exoscale.com/v2", env, zone)) + + c, err := v3.NewClient(creds, v3.ClientOptWithEndpoint(endpoint)) if err != nil { return nil, err } - return NewExoscaleProviderWithClient(client, env, zone, dryRun, opts...), nil + return NewExoscaleProviderWithClient(&exoscaleClient{c: c}, dryRun, zoneCacheDuration, opts...), nil } // NewExoscaleProviderWithClient returns ExoscaleProvider DNS provider interface implementation (Client provided) -func NewExoscaleProviderWithClient(client EgoscaleClientI, env, zone string, dryRun bool, opts ...ExoscaleOption) *ExoscaleProvider { +func NewExoscaleProviderWithClient(client EgoscaleClientI, dryRun bool, zoneCacheDuration time.Duration, opts ...ExoscaleOption) *ExoscaleProvider { ep := &ExoscaleProvider{ filter: &zoneFilter{}, OnApplyChanges: func(_ *plan.Changes) {}, domain: endpoint.NewDomainFilter([]string{""}), client: client, - apiEnv: env, - apiZone: zone, + zoneCache: blueprint.NewZoneCache[map[string]string](zoneCacheDuration), dryRun: dryRun, } for _, opt := range opts { @@ -98,20 +151,39 @@ func NewExoscaleProviderWithClient(client EgoscaleClientI, env, zone string, dry } func (ep *ExoscaleProvider) getZones(ctx context.Context) (map[string]string, error) { - ctx = exoapi.WithEndpoint(ctx, exoapi.NewReqEndpoint(ep.apiEnv, ep.apiZone)) - domains, err := ep.client.ListDNSDomains(ctx, ep.apiZone) + if !ep.zoneCache.Expired() { + return ep.zoneCache.Get(), nil + } + + domains, err := ep.client.ListDNSDomains(ctx) if err != nil { return nil, err } zones := map[string]string{} for _, domain := range domains { - zones[*domain.ID] = *domain.UnicodeName + zones[string(domain.ID)] = domain.UnicodeName } + ep.zoneCache.Reset(zones) return zones, nil } +// GetDomainFilter returns the domain filter built from the zones the provider actually manages. +func (ep *ExoscaleProvider) GetDomainFilter() endpoint.DomainFilterInterface { + zones, err := ep.getZones(context.Background()) + if err != nil { + log.Errorf("Failed to list zones for domain filter: %v", err) + return endpoint.NewDomainFilter(nil) + } + + names := make([]string, 0, len(zones)*2) + for _, name := range zones { + names = append(names, name, "."+name) + } + return endpoint.NewDomainFilter(names) +} + // ApplyChanges simply modifies DNS via exoscale API func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { ep.OnApplyChanges(changes) @@ -123,8 +195,6 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan return nil } - ctx = exoapi.WithEndpoint(ctx, exoapi.NewReqEndpoint(ep.apiEnv, ep.apiZone)) - zones, err := ep.getZones(ctx) if err != nil { return err @@ -140,20 +210,16 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan continue } - // API does not accept 0 as default TTL but wants nil pointer instead - var ttl *int64 - if epoint.RecordTTL != 0 { - t := int64(epoint.RecordTTL) - ttl = &t + req := v3.CreateDNSDomainRecordRequest{ + Name: name, + Type: v3.CreateDNSDomainRecordRequestType(epoint.RecordType), + Content: epoint.Targets[0], } - record := egoscale.DNSDomainRecord{ - Name: &name, - Type: &epoint.RecordType, - TTL: ttl, - Content: &epoint.Targets[0], + if epoint.RecordTTL != 0 { + req.Ttl = int64(epoint.RecordTTL) } - _, err := ep.client.CreateDNSDomainRecord(ctx, ep.apiZone, zoneID, &record) - if err != nil { + + if err := ep.client.CreateDNSDomainRecord(ctx, v3.UUID(zoneID), req); err != nil { return err } } @@ -168,25 +234,27 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan continue } - records, err := ep.client.ListDNSDomainRecords(ctx, ep.apiZone, zoneID) + records, err := ep.client.ListDNSDomainRecords(ctx, v3.UUID(zoneID)) if err != nil { return err } for _, record := range records { - if *record.Name != name { + if record.Name != name { + continue + } + if string(record.Type) != epoint.RecordType { continue } - record.Type = &epoint.RecordType - record.Content = &epoint.Targets[0] + req := v3.UpdateDNSDomainRecordRequest{ + Content: epoint.Targets[0], + } if epoint.RecordTTL != 0 { - ttl := int64(epoint.RecordTTL) - record.TTL = &ttl + req.Ttl = int64(epoint.RecordTTL) } - err = ep.client.UpdateDNSDomainRecord(ctx, ep.apiZone, zoneID, &record) - if err != nil { + if err := ep.client.UpdateDNSDomainRecord(ctx, v3.UUID(zoneID), record.ID, req); err != nil { return err } @@ -210,18 +278,20 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan continue } - records, err := ep.client.ListDNSDomainRecords(ctx, ep.apiZone, zoneID) + records, err := ep.client.ListDNSDomainRecords(ctx, v3.UUID(zoneID)) if err != nil { return err } for _, record := range records { - if *record.Name != name { + if record.Name != name { + continue + } + if string(record.Type) != epoint.RecordType { continue } - err = ep.client.DeleteDNSDomainRecord(ctx, ep.apiZone, zoneID, &egoscale.DNSDomainRecord{ID: record.ID}) - if err != nil { + if err := ep.client.DeleteDNSDomainRecord(ctx, v3.UUID(zoneID), record.ID); err != nil { return err } @@ -234,31 +304,35 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan // Records returns the list of endpoints func (ep *ExoscaleProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { - ctx = exoapi.WithEndpoint(ctx, exoapi.NewReqEndpoint(ep.apiEnv, ep.apiZone)) - endpoints := make([]*endpoint.Endpoint, 0) - - domains, err := ep.client.ListDNSDomains(ctx, ep.apiZone) + zones, err := ep.getZones(ctx) if err != nil { return nil, err } - for _, domain := range domains { - records, err := ep.client.ListDNSDomainRecords(ctx, ep.apiZone, *domain.ID) + endpoints := make([]*endpoint.Endpoint, 0) + + for zoneID, zoneName := range zones { + records, err := ep.client.ListDNSDomainRecords(ctx, v3.UUID(zoneID)) if err != nil { return nil, err } for _, record := range records { - if *record.Type != endpoint.RecordTypeA && *record.Type != endpoint.RecordTypeCNAME && *record.Type != endpoint.RecordTypeTXT { + if string(record.Type) != endpoint.RecordTypeA && string(record.Type) != endpoint.RecordTypeCNAME && string(record.Type) != endpoint.RecordTypeTXT { continue } - e := endpoint.NewEndpointWithTTL((*record.Name)+"."+(*domain.UnicodeName), *record.Type, endpoint.TTL(*record.TTL), *record.Content) + fqdn := zoneName + if record.Name != "" { + fqdn = record.Name + "." + zoneName + } + + e := endpoint.NewEndpointWithTTL(fqdn, string(record.Type), endpoint.TTL(record.Ttl), record.Content) endpoints = append(endpoints, e) } } - log.Infof("called Records() with %d items", len(endpoints)) + log.Infof("Called Records() with %d items", len(endpoints)) return endpoints, nil } @@ -313,6 +387,10 @@ func (f *zoneFilter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[strin matchZoneName = zoneName matchZoneID = zoneID name = strings.TrimSuffix(endpoint.DNSName, "."+zoneName) + } else if endpoint.DNSName == zoneName && len(zoneName) > len(matchZoneName) { + matchZoneName = zoneName + matchZoneID = zoneID + name = "" } } return matchZoneID, name diff --git a/provider/exoscale/exoscale_test.go b/provider/exoscale/exoscale_test.go index 4c8a435442..660f71373d 100644 --- a/provider/exoscale/exoscale_test.go +++ b/provider/exoscale/exoscale_test.go @@ -19,32 +19,32 @@ package exoscale import ( "context" "testing" + "time" - egoscale "github.com/exoscale/egoscale/v2" + v3 "github.com/exoscale/egoscale/v3" + "github.com/google/uuid" + log "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" - log "github.com/sirupsen/logrus" - "sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/plan" - - "github.com/google/uuid" ) type createRecordExoscale struct { - domainID string - record *egoscale.DNSDomainRecord + domainID v3.UUID + req v3.CreateDNSDomainRecordRequest } type deleteRecordExoscale struct { - domainID string - recordID string + domainID v3.UUID + recordID v3.UUID } type updateRecordExoscale struct { - domainID string - record *egoscale.DNSDomainRecord + domainID v3.UUID + recordID v3.UUID + req v3.UpdateDNSDomainRecordRequest } var ( @@ -53,56 +53,85 @@ var ( updateExoscale []updateRecordExoscale ) -var defaultTTL int64 = 3600 -var domainIDs = []string{uuid.New().String(), uuid.New().String(), uuid.New().String(), uuid.New().String()} -var groups = map[string][]egoscale.DNSDomainRecord{ +var domainIDs = []string{uuid.New().String(), uuid.New().String(), uuid.New().String(), uuid.New().String(), uuid.New().String()} +var groups = map[string][]v3.DNSDomainRecord{ domainIDs[0]: { - {ID: new(uuid.New().String()), Name: new("v1"), Type: new("TXT"), Content: new("test"), TTL: &defaultTTL}, - {ID: new(uuid.New().String()), Name: new("v2"), Type: new("CNAME"), Content: new("test"), TTL: &defaultTTL}, + {ID: v3.UUID(uuid.New().String()), Name: "v1", Type: v3.DNSDomainRecordTypeTXT, Content: "test", Ttl: 3600}, + {ID: v3.UUID(uuid.New().String()), Name: "v2", Type: v3.DNSDomainRecordTypeCNAME, Content: "test", Ttl: 3600}, }, domainIDs[1]: { - {ID: new(uuid.New().String()), Name: new("v2"), Type: new("A"), Content: new("test"), TTL: &defaultTTL}, - {ID: new(uuid.New().String()), Name: new("v3"), Type: new("ALIAS"), Content: new("test"), TTL: &defaultTTL}, + {ID: v3.UUID(uuid.New().String()), Name: "v2", Type: v3.DNSDomainRecordTypeA, Content: "test", Ttl: 3600}, + {ID: v3.UUID(uuid.New().String()), Name: "v3", Type: v3.DNSDomainRecordTypeALIAS, Content: "test", Ttl: 3600}, }, domainIDs[2]: { - {ID: new(uuid.New().String()), Name: new("v1"), Type: new("TXT"), Content: new("test"), TTL: &defaultTTL}, + {ID: v3.UUID(uuid.New().String()), Name: "v1", Type: v3.DNSDomainRecordTypeTXT, Content: "test", Ttl: 3600}, }, domainIDs[3]: { - {ID: new(uuid.New().String()), Name: new("v4"), Type: new("ALIAS"), Content: new("test"), TTL: &defaultTTL}, + {ID: v3.UUID(uuid.New().String()), Name: "v4", Type: v3.DNSDomainRecordTypeALIAS, Content: "test", Ttl: 3600}, + }, + // domainIDs[4] is for apex record tests + domainIDs[4]: { + {ID: v3.UUID(uuid.New().String()), Name: "", Type: v3.DNSDomainRecordTypeA, Content: "1.2.3.4", Ttl: 3600}, }, } type ExoscaleClientStub struct{} func NewExoscaleClientStub() EgoscaleClientI { - ep := &ExoscaleClientStub{} - return ep + return &ExoscaleClientStub{} } -func (ep *ExoscaleClientStub) ListDNSDomains(_ context.Context, _ string) ([]egoscale.DNSDomain, error) { - domains := []egoscale.DNSDomain{ - {ID: &domainIDs[0], UnicodeName: new("foo.com")}, - {ID: &domainIDs[1], UnicodeName: new("bar.com")}, - } - return domains, nil +func (ep *ExoscaleClientStub) ListDNSDomains(_ context.Context) ([]v3.DNSDomain, error) { + return []v3.DNSDomain{ + {ID: v3.UUID(domainIDs[0]), UnicodeName: "foo.com"}, + {ID: v3.UUID(domainIDs[1]), UnicodeName: "bar.com"}, + }, nil +} + +func (ep *ExoscaleClientStub) ListDNSDomainRecords(_ context.Context, domainID v3.UUID) ([]v3.DNSDomainRecord, error) { + return groups[string(domainID)], nil +} + +func (ep *ExoscaleClientStub) CreateDNSDomainRecord(_ context.Context, domainID v3.UUID, req v3.CreateDNSDomainRecordRequest) error { + createExoscale = append(createExoscale, createRecordExoscale{domainID: domainID, req: req}) + return nil } -func (ep *ExoscaleClientStub) ListDNSDomainRecords(_ context.Context, _, domainID string) ([]egoscale.DNSDomainRecord, error) { - return groups[domainID], nil +func (ep *ExoscaleClientStub) DeleteDNSDomainRecord(_ context.Context, domainID v3.UUID, recordID v3.UUID) error { + deleteExoscale = append(deleteExoscale, deleteRecordExoscale{domainID: domainID, recordID: recordID}) + return nil } -func (ep *ExoscaleClientStub) CreateDNSDomainRecord(_ context.Context, _, domainID string, record *egoscale.DNSDomainRecord) (*egoscale.DNSDomainRecord, error) { - createExoscale = append(createExoscale, createRecordExoscale{domainID: domainID, record: record}) - return record, nil +func (ep *ExoscaleClientStub) UpdateDNSDomainRecord(_ context.Context, domainID v3.UUID, recordID v3.UUID, req v3.UpdateDNSDomainRecordRequest) error { + updateExoscale = append(updateExoscale, updateRecordExoscale{domainID: domainID, recordID: recordID, req: req}) + return nil } -func (ep *ExoscaleClientStub) DeleteDNSDomainRecord(_ context.Context, _, domainID string, record *egoscale.DNSDomainRecord) error { - deleteExoscale = append(deleteExoscale, deleteRecordExoscale{domainID: domainID, recordID: *record.ID}) +// ExoscaleClientApexStub serves a single domain with one apex record for apex-specific tests. +type ExoscaleClientApexStub struct{} + +func (ep *ExoscaleClientApexStub) ListDNSDomains(_ context.Context) ([]v3.DNSDomain, error) { + return []v3.DNSDomain{ + {ID: v3.UUID(domainIDs[4]), UnicodeName: "apex.com"}, + }, nil +} + +func (ep *ExoscaleClientApexStub) ListDNSDomainRecords(_ context.Context, _ v3.UUID) ([]v3.DNSDomainRecord, error) { + return groups[domainIDs[4]], nil +} + +func (ep *ExoscaleClientApexStub) CreateDNSDomainRecord(_ context.Context, domainID v3.UUID, req v3.CreateDNSDomainRecordRequest) error { + createExoscale = append(createExoscale, createRecordExoscale{domainID: domainID, req: req}) return nil } -func (ep *ExoscaleClientStub) UpdateDNSDomainRecord(_ context.Context, _, domainID string, record *egoscale.DNSDomainRecord) error { - updateExoscale = append(updateExoscale, updateRecordExoscale{domainID: domainID, record: record}) +func (ep *ExoscaleClientApexStub) DeleteDNSDomainRecord(_ context.Context, domainID v3.UUID, recordID v3.UUID) error { + deleteExoscale = append(deleteExoscale, deleteRecordExoscale{domainID: domainID, recordID: recordID}) + return nil +} + +func (ep *ExoscaleClientApexStub) UpdateDNSDomainRecord(_ context.Context, domainID v3.UUID, recordID v3.UUID, req v3.UpdateDNSDomainRecordRequest) error { + updateExoscale = append(updateExoscale, updateRecordExoscale{domainID: domainID, recordID: recordID, req: req}) return nil } @@ -116,8 +145,7 @@ func contains(arr []*endpoint.Endpoint, name string) bool { } func TestExoscaleGetRecords(t *testing.T) { - provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), "", "", false) - + provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), false, 0) recs, err := provider.Records(t.Context()) if err == nil { assert.Len(t, recs, 3) @@ -131,142 +159,104 @@ func TestExoscaleGetRecords(t *testing.T) { } } +func TestExoscaleGetRecordsApex(t *testing.T) { + provider := NewExoscaleProviderWithClient(&ExoscaleClientApexStub{}, false, 0) + recs, err := provider.Records(t.Context()) + assert.NoError(t, err) + assert.Len(t, recs, 1) + // Apex record must appear as the bare zone name, not ".apex.com" + assert.True(t, contains(recs, "apex.com")) + assert.False(t, contains(recs, ".apex.com")) +} + func TestExoscaleApplyChanges(t *testing.T) { - provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), "", "", false) + provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), false, 0) - plan := &plan.Changes{ + changes := &plan.Changes{ Create: []*endpoint.Endpoint{ - { - DNSName: "v1.foo.com", - RecordType: "A", - Targets: []string{""}, - }, - { - DNSName: "v1.foobar.com", - RecordType: "TXT", - Targets: []string{""}, - }, + {DNSName: "v1.foo.com", RecordType: "A", Targets: []string{""}}, + {DNSName: "v1.foobar.com", RecordType: "TXT", Targets: []string{""}}, }, Delete: []*endpoint.Endpoint{ - { - DNSName: "v1.foo.com", - RecordType: "A", - Targets: []string{""}, - }, - { - DNSName: "v1.foobar.com", - RecordType: "TXT", - Targets: []string{""}, - }, + {DNSName: "v1.foo.com", RecordType: "TXT", Targets: []string{""}}, + {DNSName: "v1.foobar.com", RecordType: "TXT", Targets: []string{""}}, }, UpdateOld: []*endpoint.Endpoint{ - { - DNSName: "v1.foo.com", - RecordType: "A", - Targets: []string{""}, - }, - { - DNSName: "v1.foobar.com", - RecordType: "TXT", - Targets: []string{""}, - }, + {DNSName: "v1.foo.com", RecordType: "TXT", Targets: []string{""}}, + {DNSName: "v1.foobar.com", RecordType: "TXT", Targets: []string{""}}, }, UpdateNew: []*endpoint.Endpoint{ - { - DNSName: "v1.foo.com", - RecordType: "A", - Targets: []string{""}, - }, - { - DNSName: "v1.foobar.com", - RecordType: "TXT", - Targets: []string{""}, - }, + {DNSName: "v1.foo.com", RecordType: "TXT", Targets: []string{""}}, + {DNSName: "v1.foobar.com", RecordType: "TXT", Targets: []string{""}}, }, } createExoscale = make([]createRecordExoscale, 0) deleteExoscale = make([]deleteRecordExoscale, 0) + updateExoscale = make([]updateRecordExoscale, 0) - provider.ApplyChanges(t.Context(), plan) + provider.ApplyChanges(t.Context(), changes) assert.Len(t, createExoscale, 1) - assert.Equal(t, domainIDs[0], createExoscale[0].domainID) - assert.Equal(t, "v1", *createExoscale[0].record.Name) + assert.Equal(t, v3.UUID(domainIDs[0]), createExoscale[0].domainID) + assert.Equal(t, "v1", createExoscale[0].req.Name) assert.Len(t, deleteExoscale, 1) - assert.Equal(t, domainIDs[0], deleteExoscale[0].domainID) - assert.Equal(t, *groups[domainIDs[0]][0].ID, deleteExoscale[0].recordID) + assert.Equal(t, v3.UUID(domainIDs[0]), deleteExoscale[0].domainID) + assert.Equal(t, groups[domainIDs[0]][0].ID, deleteExoscale[0].recordID) assert.Len(t, updateExoscale, 1) - assert.Equal(t, domainIDs[0], updateExoscale[0].domainID) - assert.Equal(t, *groups[domainIDs[0]][0].ID, *updateExoscale[0].record.ID) + assert.Equal(t, v3.UUID(domainIDs[0]), updateExoscale[0].domainID) + assert.Equal(t, groups[domainIDs[0]][0].ID, updateExoscale[0].recordID) } -func TestExoscaleMerge_NoUpdateOnTTL0Changes(t *testing.T) { - updateOld := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeA, +func TestExoscaleApplyChangesApex(t *testing.T) { + provider := NewExoscaleProviderWithClient(&ExoscaleClientApexStub{}, false, 0) + + createExoscale = make([]createRecordExoscale, 0) + deleteExoscale = make([]deleteRecordExoscale, 0) + updateExoscale = make([]updateRecordExoscale, 0) + + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + {DNSName: "apex.com", RecordType: "A", Targets: []string{"1.2.3.4"}}, }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeA, + Delete: []*endpoint.Endpoint{ + {DNSName: "apex.com", RecordType: "A", Targets: []string{"1.2.3.4"}}, }, } + assert.NoError(t, provider.ApplyChanges(t.Context(), changes)) + + assert.Len(t, createExoscale, 1) + assert.Equal(t, v3.UUID(domainIDs[4]), createExoscale[0].domainID) + assert.Empty(t, createExoscale[0].req.Name) + + assert.Len(t, deleteExoscale, 1) + assert.Equal(t, v3.UUID(domainIDs[4]), deleteExoscale[0].domainID) + assert.Equal(t, groups[domainIDs[4]][0].ID, deleteExoscale[0].recordID) +} + +func TestExoscaleMerge_NoUpdateOnTTL0Changes(t *testing.T) { + updateOld := []*endpoint.Endpoint{ + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeA}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeA}, + } updateNew := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(0), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(0), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(0), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(0), RecordType: endpoint.RecordTypeCNAME}, } - assert.Empty(t, merge(updateOld, updateNew)) } func TestExoscaleMerge_UpdateOnTTLChanges(t *testing.T) { updateOld := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeCNAME}, } - updateNew := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(77), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(10), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(77), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(10), RecordType: endpoint.RecordTypeCNAME}, } - merged := merge(updateOld, updateNew) assert.Len(t, merged, 2) assert.Equal(t, "name1", merged[0].DNSName) @@ -274,35 +264,13 @@ func TestExoscaleMerge_UpdateOnTTLChanges(t *testing.T) { func TestExoscaleMerge_AlwaysUpdateTarget(t *testing.T) { updateOld := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(1), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(1), RecordType: endpoint.RecordTypeCNAME}, } - updateNew := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1-changed"}, - RecordTTL: endpoint.TTL(0), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(0), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1-changed"}, RecordTTL: endpoint.TTL(0), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(0), RecordType: endpoint.RecordTypeCNAME}, } - merged := merge(updateOld, updateNew) assert.Len(t, merged, 1) assert.Equal(t, "target1-changed", merged[0].Targets[0]) @@ -310,37 +278,14 @@ func TestExoscaleMerge_AlwaysUpdateTarget(t *testing.T) { func TestExoscaleMerge_NoUpdateIfTTLUnchanged(t *testing.T) { updateOld := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(55), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(55), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(55), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(55), RecordType: endpoint.RecordTypeCNAME}, } - updateNew := []*endpoint.Endpoint{ - { - DNSName: "name1", - Targets: endpoint.Targets{"target1"}, - RecordTTL: endpoint.TTL(55), - RecordType: endpoint.RecordTypeCNAME, - }, - { - DNSName: "name2", - Targets: endpoint.Targets{"target2"}, - RecordTTL: endpoint.TTL(55), - RecordType: endpoint.RecordTypeCNAME, - }, + {DNSName: "name1", Targets: endpoint.Targets{"target1"}, RecordTTL: endpoint.TTL(55), RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "name2", Targets: endpoint.Targets{"target2"}, RecordTTL: endpoint.TTL(55), RecordType: endpoint.RecordTypeCNAME}, } - - merged := merge(updateOld, updateNew) - assert.Empty(t, merged) + assert.Empty(t, merge(updateOld, updateNew)) } func TestZones(t *testing.T) { @@ -353,19 +298,15 @@ func TestZones(t *testing.T) { { name: "single matching zone", domain: "example.com", - input: map[string]string{ - "1": "example.com", - }, + input: map[string]string{"1": "example.com"}, expected: map[string]string{ "1": "example.com", }, }, { - name: "non matching zone", - domain: "example.com", - input: map[string]string{ - "1": "other.com", - }, + name: "non matching zone", + domain: "example.com", + input: map[string]string{"1": "other.com"}, expected: map[string]string{}, }, { @@ -415,10 +356,8 @@ func TestZones(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - zoneFilter := zoneFilter{ - domain: test.domain, - } - result := zoneFilter.Zones(test.input) + zf := zoneFilter{domain: test.domain} + result := zf.Zones(test.input) assert.Equal(t, test.expected, result) }) } @@ -429,19 +368,12 @@ func TestExoscaleWithDomain_SetsDomain(t *testing.T) { name string domainFilter []string }{ - { - name: "domain filter", - domainFilter: []string{"example.com", "apple.xyz"}, - }, + {name: "domain filter", domainFilter: []string{"example.com", "apple.xyz"}}, } - for _, test := range tests { t.Run(test.name, func(_ *testing.T) { p := &ExoscaleProvider{} - - df := endpoint.NewDomainFilter(test.domainFilter) - - ExoscaleWithDomain(df)(p) + ExoscaleWithDomain(endpoint.NewDomainFilter(test.domainFilter))(p) }) } } @@ -457,28 +389,159 @@ func TestInMemoryWithLogging_LogsChanges(t *testing.T) { ExoscaleWithLogging()(p) changes := &plan.Changes{ - Create: []*endpoint.Endpoint{ - {DNSName: "create.example.com", RecordType: "A"}, - }, - UpdateOld: []*endpoint.Endpoint{ - {DNSName: "old.example.com", RecordType: "A"}, - }, - UpdateNew: []*endpoint.Endpoint{ - {DNSName: "new.example.com", RecordType: "A"}, - }, - Delete: []*endpoint.Endpoint{ - {DNSName: "delete.example.com", RecordType: "A"}, - }, + Create: []*endpoint.Endpoint{{DNSName: "create.example.com", RecordType: "A"}}, + UpdateOld: []*endpoint.Endpoint{{DNSName: "old.example.com", RecordType: "A"}}, + UpdateNew: []*endpoint.Endpoint{{DNSName: "new.example.com", RecordType: "A"}}, + Delete: []*endpoint.Endpoint{{DNSName: "delete.example.com", RecordType: "A"}}, } p.OnApplyChanges(changes) entries := hook.AllEntries() - assert.Contains(t, entries[0].Message, "CREATE") assert.Contains(t, entries[1].Message, "UPDATE (old)") assert.Contains(t, entries[2].Message, "UPDATE (new)") assert.Contains(t, entries[3].Message, "DELETE") + }) +} +func TestExoscaleGetDomainFilter(t *testing.T) { + t.Run("returns bare and leading-dot variants for each zone", func(t *testing.T) { + provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), false, 0) + filter := provider.GetDomainFilter() + // stub returns foo.com and bar.com + assert.True(t, filter.Match("foo.com")) + assert.True(t, filter.Match("v1.foo.com")) + assert.True(t, filter.Match("bar.com")) + assert.True(t, filter.Match("v2.bar.com")) }) + + t.Run("returns empty filter on list error", func(t *testing.T) { + provider := NewExoscaleProviderWithClient(&errListDomainsStub{}, false, 0) + filter := provider.GetDomainFilter() + // empty filter matches nothing specific; getting a DomainFilter back is enough + assert.NotNil(t, filter) + }) +} + +func TestExoscaleApplyChangesDryRun(t *testing.T) { + provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), true, 0) + + createExoscale = make([]createRecordExoscale, 0) + deleteExoscale = make([]deleteRecordExoscale, 0) + + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "A", Targets: []string{"1.2.3.4"}}, + }, + Delete: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "A", Targets: []string{"1.2.3.4"}}, + }, + } + + assert.NoError(t, provider.ApplyChanges(t.Context(), changes)) + // dryRun: nothing should be sent to the API + assert.Empty(t, createExoscale) + assert.Empty(t, deleteExoscale) +} + +func TestExoscaleApplyChangesWithTTL(t *testing.T) { + provider := NewExoscaleProviderWithClient(NewExoscaleClientStub(), false, 0) + + createExoscale = make([]createRecordExoscale, 0) + updateExoscale = make([]updateRecordExoscale, 0) + + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "A", Targets: []string{"1.2.3.4"}, RecordTTL: 300}, + }, + UpdateOld: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "TXT", Targets: []string{"old"}, RecordTTL: 300}, + }, + UpdateNew: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "TXT", Targets: []string{"new"}, RecordTTL: 600}, + }, + } + + assert.NoError(t, provider.ApplyChanges(t.Context(), changes)) + assert.Len(t, createExoscale, 1) + assert.Equal(t, int64(300), createExoscale[0].req.Ttl) + assert.Len(t, updateExoscale, 1) + assert.Equal(t, int64(600), updateExoscale[0].req.Ttl) +} + +func TestExoscaleApplyChangesZonesError(t *testing.T) { + provider := NewExoscaleProviderWithClient(&errListDomainsStub{}, false, 0) + changes := &plan.Changes{ + Create: []*endpoint.Endpoint{ + {DNSName: "v1.foo.com", RecordType: "A", Targets: []string{"1.2.3.4"}}, + }, + } + assert.Error(t, provider.ApplyChanges(t.Context(), changes)) +} + +func TestExoscaleRecordsZonesError(t *testing.T) { + provider := NewExoscaleProviderWithClient(&errListDomainsStub{}, false, 0) + _, err := provider.Records(t.Context()) + assert.Error(t, err) +} + +func TestExoscaleRecordsListRecordsError(t *testing.T) { + provider := NewExoscaleProviderWithClient(&errListRecordsStub{}, false, 0) + _, err := provider.Records(t.Context()) + assert.Error(t, err) +} + +func TestExoscaleZoneCacheHit(t *testing.T) { + provider := NewExoscaleProviderWithClient( + NewExoscaleClientStub(), + false, + time.Hour, + ExoscaleWithDomain(endpoint.NewDomainFilter([]string{"foo.com"})), + ) + // first call populates the cache + recs1, err := provider.Records(t.Context()) + assert.NoError(t, err) + // second call hits the cache + recs2, err := provider.Records(t.Context()) + assert.NoError(t, err) + assert.Len(t, recs2, len(recs1)) +} + +// errListDomainsStub always errors on ListDNSDomains. +type errListDomainsStub struct{} + +func (s *errListDomainsStub) ListDNSDomains(_ context.Context) ([]v3.DNSDomain, error) { + return nil, assert.AnError +} +func (s *errListDomainsStub) ListDNSDomainRecords(_ context.Context, _ v3.UUID) ([]v3.DNSDomainRecord, error) { + return nil, nil +} +func (s *errListDomainsStub) CreateDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.CreateDNSDomainRecordRequest) error { + return nil +} +func (s *errListDomainsStub) DeleteDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.UUID) error { + return nil +} +func (s *errListDomainsStub) UpdateDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.UUID, _ v3.UpdateDNSDomainRecordRequest) error { + return nil +} + +// errListRecordsStub returns one domain but errors on ListDNSDomainRecords. +type errListRecordsStub struct{} + +func (s *errListRecordsStub) ListDNSDomains(_ context.Context) ([]v3.DNSDomain, error) { + return []v3.DNSDomain{{ID: v3.UUID(domainIDs[0]), UnicodeName: "foo.com"}}, nil +} +func (s *errListRecordsStub) ListDNSDomainRecords(_ context.Context, _ v3.UUID) ([]v3.DNSDomainRecord, error) { + return nil, assert.AnError +} +func (s *errListRecordsStub) CreateDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.CreateDNSDomainRecordRequest) error { + return nil +} +func (s *errListRecordsStub) DeleteDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.UUID) error { + return nil +} +func (s *errListRecordsStub) UpdateDNSDomainRecord(_ context.Context, _ v3.UUID, _ v3.UUID, _ v3.UpdateDNSDomainRecordRequest) error { + return nil }