From 77b8b532278fcad269ca24813953d5660fb95ed3 Mon Sep 17 00:00:00 2001 From: artin Date: Wed, 3 Dec 2025 03:06:33 +0800 Subject: [PATCH] ALIDNS: init --- go.mod | 3 + go.sum | 48 +++++++ integrationTest/integration_test.go | 4 + integrationTest/profiles.json | 6 + providers/_all/all.go | 1 + providers/alidns/aliDnsProvider.go | 195 ++++++++++++++++++++++++++++ providers/alidns/api.go | 112 ++++++++++++++++ providers/alidns/auditrecords.go | 18 +++ providers/alidns/convert.go | 84 ++++++++++++ providers/alidns/pagination.go | 28 ++++ 10 files changed, 499 insertions(+) create mode 100644 providers/alidns/aliDnsProvider.go create mode 100644 providers/alidns/api.go create mode 100644 providers/alidns/auditrecords.go create mode 100644 providers/alidns/convert.go create mode 100644 providers/alidns/pagination.go diff --git a/go.mod b/go.mod index 8a23d46d2..ba74e71eb 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/G-Core/gcore-dns-sdk-go v0.3.3 + github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.18 github.com/failsafe-go/failsafe-go v0.9.2 @@ -127,6 +128,7 @@ require ( github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -134,6 +136,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/peterhellberg/link v1.2.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect diff --git a/go.sum b/go.sum index 7c9625425..8fea881c0 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIi cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= @@ -30,18 +31,23 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DisposaBoy/JsonConfigReader v0.0.0-20201129172854-99cf318d67e7 h1:AJKJCKcb/psppPl/9CUiQQnTG+Bce0/cIweD5w5Q7aQ= github.com/DisposaBoy/JsonConfigReader v0.0.0-20201129172854-99cf318d67e7/go.mod h1:GCzqZQHydohgVLSIqRKZeTt8IGb1Y4NaFfim3H40uUI= github.com/G-Core/gcore-dns-sdk-go v0.3.3 h1:McILJSbJ5nOcT0MI0aBYhEuufCF329YbqKwFIN0RjCI= github.com/G-Core/gcore-dns-sdk-go v0.3.3/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/TomOnTime/utfutil v1.0.0 h1:/0Ivgo2OjXJxo8i7zgvs7ewSFZMLwCRGm3P5Umowb90= github.com/TomOnTime/utfutil v1.0.0/go.mod h1:l9lZmOniizVSuIliSkEf87qivMRlSNzbdBFKjuLRg1c= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= @@ -137,6 +143,7 @@ github.com/fbiville/markdown-table-formatter v0.3.0 h1:PIm1UNgJrFs8q1htGTw+wnnNY github.com/fbiville/markdown-table-formatter v0.3.0/go.mod h1:q89TDtSEVDdTaufgSbfHpNVdPU/bmfvqNkrC5HagmLY= 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= @@ -144,6 +151,7 @@ github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX 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= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -176,9 +184,11 @@ 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= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -201,6 +211,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw 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= @@ -248,12 +259,17 @@ github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2 github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +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= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -324,6 +340,7 @@ github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nicholas-fedor/shoutrrr v0.12.1 h1:8NjY+I3K7cGHy89ncnaPGUA0ex44XbYK3SAFJX9YMI8= github.com/nicholas-fedor/shoutrrr v0.12.1/go.mod h1:64qWuPpvTUv9ZppEoR6OdroiFmgf9w11YSaR0h9KZGg= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= github.com/nrdcg/goinwx v0.12.0 h1:ujdUqDBnaRSFwzVnImvPHYw3w3m9XgmGImNUw1GyMb4= @@ -332,6 +349,8 @@ github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/oracle/oci-go-sdk/v65 v65.105.0 h1:VN3IkW4kwyOOIrjrg7Lh1QGG/sou54c8dqTZB2THeTE= github.com/oracle/oci-go-sdk/v65 v65.105.0/go.mod h1:oB8jFGVc/7/zJ+DbleE8MzGHjhs2ioCz5stRTdZdIcY= github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE= @@ -413,6 +432,10 @@ 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.26.1 h1:MeqIjkTBBsZwWAK6giZyMkqLmKMclVHEuTNmoBdx4MA= github.com/transip/gotransip/v6 v6.26.1/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +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= @@ -456,6 +479,8 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= @@ -463,6 +488,7 @@ go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= 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= @@ -477,12 +503,22 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 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= @@ -531,6 +567,7 @@ golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -590,12 +627,15 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 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= @@ -610,8 +650,12 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -644,6 +688,7 @@ google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -662,12 +707,15 @@ 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= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index b45cb07c6..cfd17cf57 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -216,6 +216,7 @@ func makeTests() []*TestGroup { testgroup("Attl", not("LINODE"), // Linode does not support arbitrary TTLs: both are rounded up to 3600. + not("ALIDNS"), // ALIDNS auto-adjusts TTL to 600-86400 range. tc("Create Arc", ttl(a("testa", "1.1.1.1"), 333)), tc("Change TTL", ttl(a("testa", "1.1.1.1"), 999)), ), @@ -223,6 +224,7 @@ func makeTests() []*TestGroup { testgroup("TTL", not("NETCUP"), // NETCUP does not support TTLs. not("LINODE"), // Linode does not support arbitrary TTLs: 666 and 1000 are both rounded up to 3600. + not("ALIDNS"), // ALIDNS auto-adjusts TTL to 600-86400 range. tc("Start", ttl(a("@", "8.8.8.8"), 666), a("www", "1.2.3.4"), a("www", "5.6.7.8")), tc("Change a ttl", ttl(a("@", "8.8.8.8"), 1000), a("www", "1.2.3.4"), a("www", "5.6.7.8")), tc("Change single target from set", ttl(a("@", "8.8.8.8"), 1000), a("www", "2.2.2.2"), a("www", "5.6.7.8")), @@ -245,6 +247,7 @@ func makeTests() []*TestGroup { // Next we add an additional record at the same label AND change // the TTL of the existing record. testgroup("add to label and change orig ttl", + not("ALIDNS"), // ALIDNS auto-adjusts TTL to 600-86400 range. tc("Setup", ttl(a("www", "5.6.7.8"), 400)), tc("Add at same label, new ttl", ttl(a("www", "5.6.7.8"), 700), ttl(a("www", "1.2.3.4"), 700)), ), @@ -369,6 +372,7 @@ func makeTests() []*TestGroup { // RFC 7505 NullMX at Apex testgroup("NullMXApex", not( + "ALIDNS", // ALIDNS does not support NullMX. "TRANSIP", // TRANSIP is slow and doesn't support NullMX. Skip to save time. ), tc("create", // Install a Null MX. diff --git a/integrationTest/profiles.json b/integrationTest/profiles.json index 481f6f947..ec69ab81d 100644 --- a/integrationTest/profiles.json +++ b/integrationTest/profiles.json @@ -16,6 +16,12 @@ "group_id": "$AED_GROUP_ID", "host": "$AED_HOST" }, + "ALIDNS": { + "TYPE": "ALIDNS", + "access_key_id": "$ALIDNS_ACCESS_KEY_ID", + "access_key_secret": "$ALIDNS_ACCESS_KEY_SECRET", + "domain": "$ALIDNS_DOMAIN" + }, "AUTODNS": { "TYPE": "AUTODNS", "context": "$AUTODNS_CONTEXT", diff --git a/providers/_all/all.go b/providers/_all/all.go index 5e904795e..adb6a0b62 100644 --- a/providers/_all/all.go +++ b/providers/_all/all.go @@ -5,6 +5,7 @@ import ( // Define all known providers here. They should each register themselves with the providers package via init function. _ "github.com/StackExchange/dnscontrol/v4/providers/adguardhome" _ "github.com/StackExchange/dnscontrol/v4/providers/akamaiedgedns" + _ "github.com/StackExchange/dnscontrol/v4/providers/alidns" _ "github.com/StackExchange/dnscontrol/v4/providers/autodns" _ "github.com/StackExchange/dnscontrol/v4/providers/axfrddns" _ "github.com/StackExchange/dnscontrol/v4/providers/azuredns" diff --git a/providers/alidns/aliDnsProvider.go b/providers/alidns/aliDnsProvider.go new file mode 100644 index 000000000..c3a40cecd --- /dev/null +++ b/providers/alidns/aliDnsProvider.go @@ -0,0 +1,195 @@ +package alidns + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/StackExchange/dnscontrol/v4/pkg/diff" + "github.com/StackExchange/dnscontrol/v4/pkg/printer" + "github.com/StackExchange/dnscontrol/v4/providers" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" +) + +var features = providers.DocumentationNotes{ + providers.CanUseAlias: providers.Cannot(), + providers.CanUseCAA: providers.Can(), + providers.CanUsePTR: providers.Cannot(), + providers.CanUseNAPTR: providers.Cannot(), + providers.CanUseSRV: providers.Can(), + providers.CanUseSSHFP: providers.Cannot(), + providers.CanUseTLSA: providers.Cannot(), + providers.CanAutoDNSSEC: providers.Can(), + providers.CanConcur: providers.Cannot(), + + providers.DocOfficiallySupported: providers.Cannot(), + providers.DocDualHost: providers.Can(), + providers.DocCreateDomains: providers.Can(), + + providers.CanUseRoute53Alias: providers.Cannot(), +} + +func init() { + const providerName = "ALIDNS" + const providerMaintainer = "@bytemain" + fns := providers.DspFuncs{ + Initializer: newAliDnsDsp, + RecordAuditor: AuditRecords, + } + providers.RegisterDomainServiceProviderType(providerName, fns, features) + // https://www.alibabacloud.com/help/en/dns/pubz-add-parsing-record#45347620b7mi9 + // Explicit URL forwarding uses 301 (permanent redirect) or 302 (temporary redirect) + // redirection technology. The browser's address bar displays the target address, and the content displayed is from the target website. + providers.RegisterCustomRecordType("EXPLICIT_URL_FORWARDING", providerName, "") + // Implicit URL forwarding: Implicit URL Forwarding forwarding uses iframe technology. + // The domain name in the browser's address bar does not change, but the content displayed is from the target website. + providers.RegisterCustomRecordType("IMPLICIT_URL_FORWARDING", providerName, "") + providers.RegisterMaintainer(providerName, providerMaintainer) + +} + +type aliDnsDsp struct { + client *alidns.Client +} + +func newAliDnsDsp(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { + accessKeyID := config["access_key_id"] + if accessKeyID == "" { + return nil, fmt.Errorf("creds.json: access_key_id must not be empty") + } + + accessKeySecret := config["access_key_secret"] + if accessKeySecret == "" { + return nil, fmt.Errorf("creds.json: access_key_secret must not be empty") + } + + // Region ID defaults to "cn-hangzhou". The region value does not affect + // DNS management (DNS is global) but Alibaba's SDK/examples require a + // region to be provided — their docs/examples use Hangzhou: + // https://www.alibabacloud.com/help/en/dns/quick-start-1 + region := config["region_id"] + if region == "" { + region = "cn-hangzhou" + } + + client, err := alidns.NewClientWithAccessKey( + region, + accessKeyID, + accessKeySecret, + ) + if err != nil { + return nil, err + } + return &aliDnsDsp{client}, nil +} + +// GetZoneRecords returns an array of RecordConfig structs for a zone. +func (a *aliDnsDsp) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { + // Fetch all pages of domain records. + records, err := a.describeDomainRecordsAll(domain) + if err != nil { + return nil, err + } + + out := models.Records{} + for _, r := range records { + if r.Status != "ENABLE" { + continue + } + + rc, err := nativeToRecord(r, domain) + if err != nil { + return nil, err + } + + out = append(out, rc) + } + + return out, nil +} + +func (a *aliDnsDsp) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { + // Alibaba Cloud DNS requires TTL to be in the range of 600 to 86400 seconds. + // Adjust TTL values to fit within this range. + for _, r := range dc.Records { + if r.TTL < 600 { + r.TTL = 600 + } + if r.TTL > 86400 { + r.TTL = 86400 + } + } + + keysToUpdate, toReport, actualChangeCount, err := diff.NewCompat(dc).ChangedGroups(existingRecords) + if err != nil { + return nil, 0, err + } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) + + existingRecordsMap := make(map[models.RecordKey][]*models.RecordConfig) + for _, r := range existingRecords { + key := models.RecordKey{NameFQDN: r.NameFQDN, Type: r.Type} + existingRecordsMap[key] = append(existingRecordsMap[key], r) + } + + desiredRecordsMap := dc.Records.GroupedByKey() + + // Deletes must occur first. For example, if replacing a existing CNAME with an A of the same name: + // DELETE CNAME foo.example.net + // must occur before + // CREATE A foo.example.net + // because both an A and a CNAME for the same name is not allowed. + + lastCorrections := []*models.Correction{} // creates and replaces last + + for key, msg := range keysToUpdate { + existing, okExisting := existingRecordsMap[key] + desired, okDesired := desiredRecordsMap[key] + + if okExisting && !okDesired { + // In the existing map but not in the desired map: Delete + corrections = append(corrections, &models.Correction{ + Msg: strings.Join(msg, "\n "), + F: func() error { + return a.deleteRecordset(existing, dc.Name) + }, + }) + printer.Debugf("deleteRecordset: %s %s\n", key.NameFQDN, key.Type) + for _, rdata := range existing { + printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined()) + } + } else if !okExisting && okDesired { + // Not in the existing map but in the desired map: Create + lastCorrections = append(lastCorrections, &models.Correction{ + Msg: strings.Join(msg, "\n "), + F: func() error { + return a.createRecordset(desired, dc.Name) + }, + }) + printer.Debugf("createRecordset: %s %s\n", key.NameFQDN, key.Type) + for _, rdata := range desired { + printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined()) + } + } else if okExisting && okDesired { + // In the existing map and in the desired map: Replace + lastCorrections = append(lastCorrections, &models.Correction{ + Msg: strings.Join(msg, "\n "), + F: func() error { + return a.updateRecordset(existing, desired, dc.Name) + }, + }) + printer.Debugf("updateRecordset: %s %s\n", key.NameFQDN, key.Type) + for _, rdata := range desired { + printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined()) + } + } + } + + // Append creates and updates after deletes + corrections = append(corrections, lastCorrections...) + + printer.Debugf("Found %d corrections (actualChangeCount=%d)\n", len(corrections), actualChangeCount) + return corrections, actualChangeCount, nil +} diff --git a/providers/alidns/api.go b/providers/alidns/api.go new file mode 100644 index 000000000..aaf1e256a --- /dev/null +++ b/providers/alidns/api.go @@ -0,0 +1,112 @@ +package alidns + +import ( + "fmt" + + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" +) + +func (a *aliDnsDsp) GetNameservers(domain string) ([]*models.Nameserver, error) { + req := alidns.CreateDescribeDomainInfoRequest() + req.DomainName = domain + + resp, err := a.client.DescribeDomainInfo(req) + if err != nil { + return nil, err + } + + return models.ToNameservers(resp.DnsServers.DnsServer) +} + +func (a *aliDnsDsp) deleteRecordset(records []*models.RecordConfig, domainName string) error { + for _, r := range records { + req := alidns.CreateDeleteDomainRecordRequest() + original, ok := r.Original.(*alidns.Record) + if !ok { + return fmt.Errorf("deleteRecordset: record original is not of type *alidns.Record") + } + req.RecordId = original.RecordId + + _, err := a.client.DeleteDomainRecord(req) + if err != nil { + return err + } + } + return nil +} + +func (a *aliDnsDsp) createRecordset(records []*models.RecordConfig, domainName string) error { + for _, r := range records { + req := alidns.CreateAddDomainRecordRequest() + req.DomainName = domainName + req.RR = r.Name + req.Type = r.Type + req.TTL = requests.Integer(fmt.Sprintf("%d", r.TTL)) + req.Value = recordToNativeContent(r) + + // Set priority for MX and SRV records + if r.Type == "MX" || r.Type == "SRV" { + req.Priority = requests.Integer(fmt.Sprintf("%d", recordToNativePriority(r))) + } + + fmt.Printf("DEBUG createRecordset: domain=%s, RR=%s, Type=%s, Value=%s, TTL=%s\n", + req.DomainName, req.RR, req.Type, req.Value, req.TTL) + + resp, err := a.client.AddDomainRecord(req) + if err != nil { + fmt.Printf("DEBUG createRecordset error: %v\n", err) + return err + } + fmt.Printf("DEBUG createRecordset success: RecordId=%s\n", resp.RecordId) + } + return nil +} + +func (a *aliDnsDsp) updateRecordset(existing, desired []*models.RecordConfig, domainName string) error { + // Strategy: Delete all existing records, then create all desired records. + // This is the simplest and most reliable approach because: + // 1. The number of records in a recordset may change + // 2. There's no guaranteed 1:1 mapping between existing and desired records + // 3. Alibaba Cloud API requires RecordId for updates, which desired records don't have + + // Delete all existing records first + if err := a.deleteRecordset(existing, domainName); err != nil { + return err + } + + // Then create all desired records + return a.createRecordset(desired, domainName) +} + +// describeDomainRecordsAll fetches all domain records for 'domain', handling +// pagination transparently. It returns the slice of *alidns.Record or an error. +func (a *aliDnsDsp) describeDomainRecordsAll(domain string) ([]*alidns.Record, error) { + // The SDK returns a slice of value Records (not pointers). We fetch pages + // as values and then convert to pointers before returning. + fetch := func(pageNumber, pageSize int) ([]alidns.Record, int, error) { + req := alidns.CreateDescribeDomainRecordsRequest() + req.DomainName = domain + req.PageNumber = requests.NewInteger(pageNumber) + req.PageSize = requests.NewInteger(pageSize) + + resp, err := a.client.DescribeDomainRecords(req) + if err != nil { + return nil, 0, err + } + + total := int(resp.TotalCount) + return resp.DomainRecords.Record, total, nil + } + + vals, err := paginateAll(fetch) + if err != nil { + return nil, err + } + out := make([]*alidns.Record, 0, len(vals)) + for i := range vals { + out = append(out, &vals[i]) + } + return out, nil +} diff --git a/providers/alidns/auditrecords.go b/providers/alidns/auditrecords.go new file mode 100644 index 000000000..1790f79d3 --- /dev/null +++ b/providers/alidns/auditrecords.go @@ -0,0 +1,18 @@ +package alidns + +import ( + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/StackExchange/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. +func AuditRecords(records []*models.RecordConfig) []error { + a := rejectif.Auditor{} + + a.Add("MX", rejectif.MxNull) // Last verified at 2025-12-03 + a.Add("TXT", rejectif.TxtIsEmpty) // Last verified at 2025-12-03 + a.Add("TXT", rejectif.TxtLongerThan(512)) // Last verified at 2025-12-03: 511 bytes OK, 764 bytes failed + return a.Audit(records) +} diff --git a/providers/alidns/convert.go b/providers/alidns/convert.go new file mode 100644 index 000000000..e8df17489 --- /dev/null +++ b/providers/alidns/convert.go @@ -0,0 +1,84 @@ +package alidns + +import ( + "fmt" + "strings" + + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" +) + +// nativeToRecord converts an Alibaba Cloud DNS record to a RecordConfig. +func nativeToRecord(r *alidns.Record, domain string) (*models.RecordConfig, error) { + rc := &models.RecordConfig{ + TTL: uint32(r.TTL), + Original: r, + } + rc.SetLabel(r.RR, domain) + + // Normalize CNAME, MX, NS records with trailing dot to be consistent with FQDN format. + value := r.Value + if r.Type == "CNAME" || r.Type == "MX" || r.Type == "NS" || r.Type == "SRV" { + if value != "" && value != "." && !strings.HasSuffix(value, ".") { + value = value + "." + } + } + + switch r.Type { + case "MX": + if err := rc.SetTargetMX(uint16(r.Priority), value); err != nil { + return nil, fmt.Errorf("unparsable MX record received from ALIDNS: %w", err) + } + case "SRV": + // SRV records in Alibaba Cloud: Priority and Weight are in separate fields, + // Value contains "port target" (e.g., "5060 sipserver.example.com") + if err := rc.PopulateFromString(r.Type, fmt.Sprintf("%d %d %s", r.Priority, r.Weight, r.Value), domain); err != nil { + return nil, fmt.Errorf("unparsable SRV record received from ALIDNS: %w", err) + } + case "CAA": + // CAA format in Alibaba: "0 issue letsencrypt.org" + if err := rc.PopulateFromString(r.Type, r.Value, domain); err != nil { + return nil, fmt.Errorf("unparsable CAA record received from ALIDNS: %w", err) + } + case "TXT": + if err := rc.SetTargetTXT(r.Value); err != nil { + return nil, fmt.Errorf("unparsable TXT record received from ALIDNS: %w", err) + } + default: + rc.Type = r.Type + if err := rc.SetTarget(value); err != nil { + return nil, fmt.Errorf("unparsable record received from ALIDNS: %w", err) + } + } + + return rc, nil +} + +// recordToNativeContent converts a RecordConfig to the Value format expected by Alibaba Cloud DNS API. +func recordToNativeContent(r *models.RecordConfig) string { + switch r.Type { + case "MX": + return r.GetTargetField() + case "SRV": + // Alibaba Cloud SRV format: "weight port target" + return fmt.Sprintf("%d %d %s", r.SrvWeight, r.SrvPort, r.GetTargetField()) + case "CAA": + return fmt.Sprintf("%d %s %s", r.CaaFlag, r.CaaTag, r.GetTargetField()) + case "TXT": + return r.GetTargetTXTJoined() + default: + return r.GetTargetField() + } +} + +// recordToNativePriority returns the priority value for MX and SRV records. +func recordToNativePriority(r *models.RecordConfig) int64 { + switch r.Type { + case "MX": + return int64(r.MxPreference) + case "SRV": + return int64(r.SrvPriority) + default: + return 0 + } +} diff --git a/providers/alidns/pagination.go b/providers/alidns/pagination.go new file mode 100644 index 000000000..0224687f3 --- /dev/null +++ b/providers/alidns/pagination.go @@ -0,0 +1,28 @@ +package alidns + +// paginateAll is a small generic paginator helper. The caller provides a +// fetch function that requests a single page (pageNumber,pageSize) and +// returns the items for that page, the total number of items available, +// and an error if any. paginateAll will iterate pages until it has +// collected all items or an error occurs. +func paginateAll[T any](fetch func(pageNumber, pageSize int) ([]T, int, error)) ([]T, error) { + const maxPageSize = 500 // Alibaba API max for many endpoints + page := 1 + pageSize := maxPageSize + var out []T + + for { + items, total, err := fetch(page, pageSize) + if err != nil { + return nil, err + } + out = append(out, items...) + + // If we've collected all items, or the page returned nothing, stop. + if len(out) >= total || len(items) == 0 { + break + } + page++ + } + return out, nil +}