mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-12-09 13:46:07 +08:00
ALIDNS: Implement ALIDNS Provider (#3878)
<!--
## Before submiting a pull request
Please make sure you've run the following commands from the root
directory.
bin/generate-all.sh
(this runs commands like "go generate", fixes formatting, and so on)
## Release changelog section
Help keep the release changelog clear by pre-naming the proper section
in the GitHub pull request title.
Some examples:
* CICD: Add required GHA permissions for goreleaser
* DOCS: Fixed providers with "contributor support" table
* ROUTE53: Allow R53_ALIAS records to enable target health evaluation
More examples/context can be found in the file .goreleaser.yml under the
'build' > 'changelog' key.
!-->
https://github.com/StackExchange/dnscontrol/issues/420
Please create the GitHub label 'provider-ALIDNS'
---------
Co-authored-by: Tom Limoncelli <6293917+tlimoncelli@users.noreply.github.com>
This commit is contained in:
parent
6153e3bac9
commit
bcef7f52fc
21 changed files with 896 additions and 3 deletions
6
.github/workflows/pr_integration_tests.yml
vendored
6
.github/workflows/pr_integration_tests.yml
vendored
|
|
@ -52,7 +52,7 @@ jobs:
|
|||
Write-Host "Integration test providers: $Providers"
|
||||
echo "integration_test_providers=$(ConvertTo-Json -InputObject $Providers -Compress)" >> $env:GITHUB_OUTPUT
|
||||
env:
|
||||
PROVIDERS: "['AXFRDDNS', 'AXFRDDNS_DNSSEC', 'AZURE_DNS','BIND','BUNNY_DNS','CLOUDFLAREAPI','CLOUDNS','CNR','DIGITALOCEAN','FORTIGATE','GANDI_V5','GCLOUD','HEDNS','HETZNER_V2','HEXONET','HUAWEICLOUD','INWX','JOKER','MYTHICBEASTS', 'NAMEDOTCOM','NS1','POWERDNS','ROUTE53','SAKURACLOUD','TRANSIP']"
|
||||
PROVIDERS: "['ALIDNS', 'AXFRDDNS', 'AXFRDDNS_DNSSEC', 'AZURE_DNS','BIND','BUNNY_DNS','CLOUDFLAREAPI','CLOUDNS','CNR','DIGITALOCEAN','FORTIGATE','GANDI_V5','GCLOUD','HEDNS','HETZNER_V2','HEXONET','HUAWEICLOUD','INWX','JOKER','MYTHICBEASTS', 'NAMEDOTCOM','NS1','POWERDNS','ROUTE53','SAKURACLOUD','TRANSIP']"
|
||||
ENV_CONTEXT: ${{ toJson(env) }}
|
||||
VARS_CONTEXT: ${{ toJson(vars) }}
|
||||
SECRETS_CONTEXT: ${{ toJson(secrets) }}
|
||||
|
|
@ -73,6 +73,7 @@ jobs:
|
|||
# PROVIDER DOMAIN LIST
|
||||
# These providers will be tested if the env variable is set.
|
||||
# Set it to the domain name to use during the test.
|
||||
ALIDNS_DOMAIN: ${{ vars.ALIDNS_DOMAIN }}
|
||||
AXFRDDNS_DOMAIN: ${{ vars.AXFRDDNS_DOMAIN }}
|
||||
AXFRDDNS_DNSSEC_DOMAIN: ${{ vars.AXFRDDNS_DNSSEC_DOMAIN }}
|
||||
AZURE_DNS_DOMAIN: ${{ vars.AZURE_DNS_DOMAIN }}
|
||||
|
|
@ -103,6 +104,9 @@ jobs:
|
|||
# The above providers have additional env variables they
|
||||
# need for credentials and such.
|
||||
#
|
||||
ALIDNS_ACCESS_KEY_ID: ${{ secrets.ALIDNS_ACCESS_KEY_ID }}
|
||||
ALIDNS_ACCESS_KEY_SECRET: ${{ secrets.ALIDNS_ACCESS_KEY_SECRET }}
|
||||
#
|
||||
AXFRDDNS_MASTER: ${{ secrets.AXFRDDNS_MASTER }}
|
||||
AXFRDDNS_NAMESERVERS: ${{ secrets.AXFRDDNS_NAMESERVERS }}
|
||||
AXFRDDNS_TRANSFER_KEY: ${{ secrets.AXFRDDNS_TRANSFER_KEY }}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ changelog:
|
|||
regexp: "(?i)^.*(major|new provider|feature)[(\\w)]*:+.*$"
|
||||
order: 1
|
||||
- title: 'Provider-specific changes:'
|
||||
regexp: "(?i)((adguardhome|akamaiedge|autodns|axfrd|azure|azure_private_dns|bind|bunnydns|cloudflare|cloudflareapi_old|cloudns|cnr|cscglobal|desec|digitalocean|dnsimple|dnsmadeeasy|doh|domainnameshop|dynadot|easyname|exoscale|fortigate|gandi|gcloud|gcore|hedns|hetzner|hetznerv2|hexonet|hostingde|huaweicloud|inwx|joker|linode|loopia|luadns|mythicbeasts|namecheap|namedotcom|netcup|netlify|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|realtimeregister|route53|rwth|sakuracloud|softlayer|transip|vercel|vultr).*:)+.*"
|
||||
regexp: "(?i)((adguardhome|akamaiedge|autodns|axfrd|azure|azure_private_dns|alidns|bind|bunnydns|cloudflare|cloudflareapi_old|cloudns|cnr|cscglobal|desec|digitalocean|dnsimple|dnsmadeeasy|doh|domainnameshop|dynadot|easyname|exoscale|fortigate|gandi|gcloud|gcore|hedns|hetzner|hetznerv2|hexonet|hostingde|huaweicloud|inwx|joker|linode|loopia|luadns|mythicbeasts|namecheap|namedotcom|netcup|netlify|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|realtimeregister|route53|rwth|sakuracloud|softlayer|transip|vercel|vultr).*:)+.*"
|
||||
order: 2
|
||||
- title: 'Documentation:'
|
||||
regexp: "(?i)^.*(docs)[(\\w)]*:+.*$"
|
||||
|
|
|
|||
1
OWNERS
1
OWNERS
|
|
@ -1,5 +1,6 @@
|
|||
providers/adguardhome @ishanjain28
|
||||
providers/akamaiedgedns @edglynes
|
||||
providers/alidns @bytemain
|
||||
providers/autodns @arnoschoon
|
||||
providers/axfrddns @hnrgrgr
|
||||
providers/azuredns @vatsalyagoel
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Currently supported DNS providers:
|
|||
|
||||
- AdGuard Home
|
||||
- Akamai Edge DNS
|
||||
- Alibaba Cloud DNS (ALIDNS)
|
||||
- AutoDNS
|
||||
- AWS Route 53
|
||||
- AXFR+DDNS
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ func GetZone(args GetZoneArgs) error {
|
|||
return fmt.Errorf("failed GetZone CDP: %w", err)
|
||||
}
|
||||
|
||||
// Get the actual provider type name from creds.json or args
|
||||
providerType := args.ProviderName
|
||||
if providerType == "" || providerType == "-" {
|
||||
providerType = providerConfigs[args.CredName][pproviderTypeFieldName]
|
||||
}
|
||||
|
||||
// decide which zones we need to convert
|
||||
zones := args.ZoneNames
|
||||
if len(args.ZoneNames) == 1 && args.ZoneNames[0] == "all" {
|
||||
|
|
@ -253,6 +259,13 @@ func GetZone(args GetZoneArgs) error {
|
|||
if defaultTTL == 0 {
|
||||
defaultTTL = prettyzone.MostCommonTTL(recs)
|
||||
}
|
||||
// If provider has a registered default TTL and no records exist or MostCommonTTL returns 0,
|
||||
// use the provider's default TTL
|
||||
if defaultTTL == 0 || defaultTTL == models.DefaultTTL {
|
||||
if providerDefaultTTL := providers.GetDefaultTTL(providerType); providerDefaultTTL > 0 {
|
||||
defaultTTL = providerDefaultTTL
|
||||
}
|
||||
}
|
||||
if defaultTTL != models.DefaultTTL && defaultTTL != 0 {
|
||||
o = append(o, fmt.Sprintf("DefaultTTL(%d)", defaultTTL))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
* [Supported providers](provider/index.md)
|
||||
* [AdGuard Home](provider/adguardhome.md)
|
||||
* [Akamai Edge DNS](provider/akamaiedgedns.md)
|
||||
* [Alibaba Cloud DNS (ALIDNS)](provider/alidns.md)
|
||||
* [Amazon Route 53](provider/route53.md)
|
||||
* [AutoDNS](provider/autodns.md)
|
||||
* [AXFR+DDNS](provider/axfrddns.md)
|
||||
|
|
|
|||
126
documentation/provider/alidns.md
Normal file
126
documentation/provider/alidns.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
## Configuration
|
||||
|
||||
This provider is for [Alibaba Cloud DNS](https://www.alibabacloud.com/product/dns) (also known as ALIDNS). To use this provider, add an entry to `creds.json` with `TYPE` set to `ALIDNS` along with your API credentials.
|
||||
|
||||
Example:
|
||||
|
||||
{% code title="creds.json" %}
|
||||
```json
|
||||
{
|
||||
"alidns": {
|
||||
"TYPE": "ALIDNS",
|
||||
"access_key_id": "YOUR_ACCESS_KEY_ID",
|
||||
"access_key_secret": "YOUR_ACCESS_KEY_SECRET"
|
||||
}
|
||||
}
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Optionally, you can specify a `region_id`:
|
||||
|
||||
{% code title="creds.json" %}
|
||||
```json
|
||||
{
|
||||
"alidns": {
|
||||
"TYPE": "ALIDNS",
|
||||
"access_key_id": "YOUR_ACCESS_KEY_ID",
|
||||
"access_key_secret": "YOUR_ACCESS_KEY_SECRET",
|
||||
"region_id": "cn-hangzhou"
|
||||
}
|
||||
}
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Note: The `region_id` defaults to `"cn-hangzhou"`. The region value does not affect DNS management (DNS is global), but Alibaba's SDK requires a region to be provided.
|
||||
|
||||
## Usage
|
||||
|
||||
An example configuration:
|
||||
|
||||
{% code title="dnsconfig.js" %}
|
||||
```javascript
|
||||
var REG_NONE = NewRegistrar("none");
|
||||
var DSP_ALIDNS = NewDnsProvider("alidns");
|
||||
|
||||
D("example.com", REG_NONE, DnsProvider(DSP_ALIDNS),
|
||||
A("test", "1.2.3.4"),
|
||||
CNAME("www", "example.com."),
|
||||
MX("@", 10, "mail.example.com."),
|
||||
);
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
## Activation
|
||||
|
||||
DNSControl depends on an Alibaba Cloud [RAM user](https://www.alibabacloud.com/help/en/ram/user-guide/overview-of-ram-users) with permissions to manage DNS records.
|
||||
|
||||
### Creating RAM User and Access Keys
|
||||
|
||||
1. Log in to the [RAM console](https://ram.console.aliyun.com/)
|
||||
2. Create a new RAM user or use an existing one
|
||||
3. Generate an AccessKey ID and AccessKey Secret for the user
|
||||
4. Attach the `AliyunDNSFullAccess` policy to the user
|
||||
|
||||
The minimum required permissions are:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "1",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"alidns:DescribeDomains",
|
||||
"alidns:DescribeDomainRecords",
|
||||
"alidns:DescribeDomainInfo",
|
||||
"alidns:AddDomainRecord",
|
||||
"alidns:UpdateDomainRecord",
|
||||
"alidns:DeleteDomainRecord"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
### TTL Constraints
|
||||
|
||||
Alibaba Cloud DNS has different TTL constraints depending on your DNS edition:
|
||||
|
||||
- **Enterprise Ultimate Edition**: TTL can be as low as 1 second (1-86400)
|
||||
- **Personal Edition / Free Edition**: Minimum TTL is 600 seconds (600-86400)
|
||||
|
||||
DNSControl will automatically validate TTL values based on your domain's edition. If you attempt to use a TTL below the minimum for your edition, you will receive an error.
|
||||
|
||||
### Chinese Domain Name Support
|
||||
|
||||
ALIDNS supports Chinese domain names (IDN with Chinese characters). However:
|
||||
|
||||
- **Supported**: ASCII characters and Chinese characters (CJK Unified Ideographs)
|
||||
- **Not supported**: Other Unicode characters (e.g., German umlauts, Arabic script)
|
||||
|
||||
DNSControl will automatically convert between punycode and unicode as needed.
|
||||
|
||||
### Record Type Support
|
||||
|
||||
The following record types are supported:
|
||||
- A, AAAA, CNAME, MX, TXT, NS
|
||||
- CAA (requires quoted values: `0 issue "letsencrypt.org"`)
|
||||
- SRV
|
||||
|
||||
### TXT Record Constraints
|
||||
|
||||
Alibaba Cloud DNS has specific constraints for TXT records:
|
||||
- Cannot be empty
|
||||
- Maximum length: 512 bytes
|
||||
- Cannot contain unescaped double quotes
|
||||
- Cannot have trailing spaces
|
||||
- Cannot have unpaired backslashes (odd number of consecutive backslashes)
|
||||
|
||||
DNSControl will audit and reject records that violate these constraints.
|
||||
|
||||
## New Domains
|
||||
|
||||
If a domain does not exist in your Alibaba Cloud account, you must create it manually through the Alibaba Cloud console. DNSControl does not automatically create new domains for this provider.
|
||||
|
|
@ -27,6 +27,7 @@ Jump to a table:
|
|||
| ------------- | ---------------- | ------------ | --------- |
|
||||
| [`ADGUARDHOME`](adguardhome.md) | ❌ | ✅ | ❌ |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ❌ | ✅ | ❌ |
|
||||
| [`ALIDNS`](alidns.md) | ❌ | ✅ | ❌ |
|
||||
| [`AUTODNS`](autodns.md) | ❌ | ✅ | ✅ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ❌ | ✅ | ❌ |
|
||||
| [`AZURE_DNS`](azure_dns.md) | ✅ | ✅ | ❌ |
|
||||
|
|
@ -90,6 +91,7 @@ Jump to a table:
|
|||
| ------------- | -------------------------------------------------------------------- | ---------------------------------------------- | -------------- | --------- |
|
||||
| [`ADGUARDHOME`](adguardhome.md) | ❔ | ❔ | ❌ | ❌ |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ❔ | ✅ | ✅ | ✅ |
|
||||
| [`ALIDNS`](alidns.md) | ✅ | ❌ | ❌ | ✅ |
|
||||
| [`AUTODNS`](autodns.md) | ✅ | ❌ | ❌ | ✅ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ✅ | ❌ | ❌ | ❌ |
|
||||
| [`AZURE_DNS`](azure_dns.md) | ✅ | ✅ | ✅ | ✅ |
|
||||
|
|
@ -152,6 +154,7 @@ Jump to a table:
|
|||
| ------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ |
|
||||
| [`ADGUARDHOME`](adguardhome.md) | ✅ | ❔ | ❔ | ❔ | ❔ |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ✅ | ❔ | ✅ | ✅ | ❌ |
|
||||
| [`ALIDNS`](alidns.md) | ❌ | ❔ | ❔ | ❌ | ❔ |
|
||||
| [`AUTODNS`](autodns.md) | ✅ | ❔ | ❔ | ✅ | ❔ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ❌ | ✅ | ✅ | ✅ | ❌ |
|
||||
| [`AZURE_DNS`](azure_dns.md) | ❌ | ❔ | ❌ | ✅ | ❔ |
|
||||
|
|
@ -208,6 +211,7 @@ Jump to a table:
|
|||
| Provider name | [`DHCID`](../language-reference/domain-modifiers/DHCID.md) | [`NAPTR`](../language-reference/domain-modifiers/NAPTR.md) | [`SRV`](../language-reference/domain-modifiers/SRV.md) | [`SVCB`](../language-reference/domain-modifiers/SVCB.md) |
|
||||
| ------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------- |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ❔ | ✅ | ✅ | ❔ |
|
||||
| [`ALIDNS`](alidns.md) | ❔ | ❌ | ✅ | ❔ |
|
||||
| [`AUTODNS`](autodns.md) | ❔ | ❔ | ✅ | ❔ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ✅ | ✅ | ✅ | ✅ |
|
||||
| [`AZURE_DNS`](azure_dns.md) | ❔ | ❌ | ✅ | ❔ |
|
||||
|
|
@ -263,6 +267,7 @@ Jump to a table:
|
|||
| Provider name | [`CAA`](../language-reference/domain-modifiers/CAA.md) | [`HTTPS`](../language-reference/domain-modifiers/HTTPS.md) | [`SMIMEA`](../language-reference/domain-modifiers/SMIMEA.md) | [`SSHFP`](../language-reference/domain-modifiers/SSHFP.md) | [`TLSA`](../language-reference/domain-modifiers/TLSA.md) |
|
||||
| ------------- | ------------------------------------------------------ | ---------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ✅ | ❔ | ❔ | ✅ | ✅ |
|
||||
| [`ALIDNS`](alidns.md) | ✅ | ❔ | ❔ | ❌ | ❌ |
|
||||
| [`AUTODNS`](autodns.md) | ✅ | ❔ | ❔ | ❌ | ❌ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ✅ | ✅ | ❔ | ✅ | ✅ |
|
||||
| [`AZURE_DNS`](azure_dns.md) | ✅ | ❔ | ❔ | ❌ | ❌ |
|
||||
|
|
@ -316,6 +321,7 @@ Jump to a table:
|
|||
| Provider name | [`AUTODNSSEC`](../language-reference/domain-modifiers/AUTODNSSEC_ON.md) | [`DNSKEY`](../language-reference/domain-modifiers/DNSKEY.md) | [`DS`](../language-reference/domain-modifiers/DS.md) |
|
||||
| ------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
|
||||
| [`AKAMAIEDGEDNS`](akamaiedgedns.md) | ✅ | ❔ | ❌ |
|
||||
| [`ALIDNS`](alidns.md) | ❌ | ❔ | ❔ |
|
||||
| [`AUTODNS`](autodns.md) | ❔ | ❔ | ❌ |
|
||||
| [`AXFRDDNS`](axfrddns.md) | ✅ | ❌ | ✅ |
|
||||
| [`BIND`](bind.md) | ✅ | ✅ | ✅ |
|
||||
|
|
@ -391,6 +397,7 @@ Providers in this category and their maintainers are:
|
|||
|Name|Maintainer|
|
||||
|---|---|
|
||||
|[`ADGUARDHOME`](adguardhome.md)|@ishanjain28|
|
||||
|[`ALIDNS`](alidns.md)|@bytemain|
|
||||
|[`AZURE_PRIVATE_DNS`](azure_private_dns.md)|@matthewmgamble|
|
||||
|[`AKAMAIEDGEDNS`](akamaiedgedns.md)|@edglynes|
|
||||
|[`AXFRDDNS`](axfrddns.md)|@hnrgrgr|
|
||||
|
|
@ -445,7 +452,6 @@ code to support this provider, we'd be glad to help in any way.
|
|||
*(The list below is sorted alphabetically.)*
|
||||
|
||||
* [1984 Hosting](https://github.com/StackExchange/dnscontrol/issues/1251) (#1251)
|
||||
* [Alibaba Cloud DNS](https://github.com/StackExchange/dnscontrol/issues/420)(#420)
|
||||
* [BookMyName](https://github.com/StackExchange/dnscontrol/issues/3451) (#3451)
|
||||
* [Constellix (DNSMadeEasy)](https://github.com/StackExchange/dnscontrol/issues/842) (#842)
|
||||
* [CoreDNS](https://github.com/StackExchange/dnscontrol/issues/1284) (#1284)
|
||||
|
|
|
|||
3
go.mod
3
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
|
||||
|
|
|
|||
48
go.sum
48
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=
|
||||
|
|
|
|||
|
|
@ -604,6 +604,7 @@ func makeTests() []*TestGroup {
|
|||
// SOFTLAYER: fails at direct internationalization, punycode works, of course.
|
||||
tc("Internationalized name", a("ööö", "1.2.3.4")),
|
||||
tc("Change IDN", a("ööö", "2.2.2.2")),
|
||||
tc("Chinese label", a("中文", "1.2.3.4")),
|
||||
tc("Internationalized CNAME Target", cname("a", "ööö.com.")),
|
||||
),
|
||||
testgroup("IDNAs in CNAME targets",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -32,12 +32,20 @@ func (aud *Auditor) Audit(records models.Records) (errs []error) {
|
|||
|
||||
// For each record, call the checks for that type, gather errors.
|
||||
for _, rc := range records {
|
||||
// First, run type-specific checks
|
||||
for _, f := range aud.checksFor[rc.Type] {
|
||||
e := f(rc)
|
||||
if e != nil {
|
||||
errs = append(errs, e)
|
||||
}
|
||||
}
|
||||
// Then, run wildcard checks that apply to all record types
|
||||
for _, f := range aud.checksFor["*"] {
|
||||
e := f(rc)
|
||||
if e != nil {
|
||||
errs = append(errs, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
|
|
|
|||
|
|
@ -18,6 +18,29 @@ func TxtHasBackslash(rc *models.RecordConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TxtHasUnpairedBackslash audits TXT records for strings that contain an odd number of consecutive backslashes.
|
||||
// Some providers strip single backslashes or convert odd consecutive backslashes to even.
|
||||
// e.g., "1back\slash" -> "1backslash", "3back\\\slash" -> "3back\\slash"
|
||||
func TxtHasUnpairedBackslash(rc *models.RecordConfig) error {
|
||||
txt := rc.GetTargetTXTJoined()
|
||||
i := 0
|
||||
for i < len(txt) {
|
||||
if txt[i] == '\\' {
|
||||
count := 0
|
||||
for i < len(txt) && txt[i] == '\\' {
|
||||
count++
|
||||
i++
|
||||
}
|
||||
if count%2 == 1 {
|
||||
return errors.New("txtstring contains unpaired backslash (odd count)")
|
||||
}
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TxtHasBackticks audits TXT records for strings that contain backticks.
|
||||
func TxtHasBackticks(rc *models.RecordConfig) error {
|
||||
if strings.Contains(rc.GetTargetTXTJoined(), "`") {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
249
providers/alidns/aliDnsProvider.go
Normal file
249
providers/alidns/aliDnsProvider.go
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
package alidns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
|
||||
"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.CanGetZones: providers.Can(),
|
||||
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.Cannot(),
|
||||
providers.CanConcur: providers.Can(),
|
||||
providers.DocOfficiallySupported: providers.Cannot(),
|
||||
providers.DocDualHost: providers.Can("Alibaba Cloud DNS allows full management of apex NS records"),
|
||||
providers.DocCreateDomains: providers.Cannot(),
|
||||
providers.CanUseRoute53Alias: providers.Cannot(),
|
||||
}
|
||||
|
||||
func init() {
|
||||
const providerName = "ALIDNS"
|
||||
const providerMaintainer = "@bytemain"
|
||||
fns := providers.DspFuncs{
|
||||
Initializer: newAliDNSDsp,
|
||||
RecordAuditor: AuditRecords,
|
||||
}
|
||||
providers.RegisterDomainServiceProviderType(providerName, fns, features)
|
||||
providers.RegisterMaintainer(providerName, providerMaintainer)
|
||||
// Register default TTL of 600 seconds (10 minutes) for Alibaba Cloud DNS
|
||||
// This is the minimum TTL for free/personal edition domains
|
||||
providers.RegisterDefaultTTL(providerName, 600)
|
||||
|
||||
}
|
||||
|
||||
type aliDNSDsp struct {
|
||||
client *alidns.Client
|
||||
domainVersionCache map[string]*domainVersionInfo
|
||||
cacheMu sync.Mutex
|
||||
}
|
||||
|
||||
type domainVersionInfo struct {
|
||||
versionCode string
|
||||
minTTL uint32
|
||||
maxTTL uint32
|
||||
}
|
||||
|
||||
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: client,
|
||||
domainVersionCache: make(map[string]*domainVersionInfo),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *aliDNSDsp) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||
nsStrings, err := a.getNameservers(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return models.ToNameserversStripTD(nsStrings)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Alibaba Cloud's DescribeDomainRecords API doesn't return NS records at the apex.
|
||||
// We need to fetch them separately using getNameservers and add them to the records.
|
||||
nameservers, err := a.getNameservers(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ns := range nameservers {
|
||||
rc := nativeToRecordNS(ns, domain)
|
||||
out = append(out, rc)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *aliDNSDsp) ListZones() ([]string, error) {
|
||||
return a.describeDomainsAll()
|
||||
}
|
||||
|
||||
func removeTrailingDot(record string) string {
|
||||
return strings.TrimSuffix(record, ".")
|
||||
}
|
||||
|
||||
func deduplicateNameServerTargets(newRecs models.Records) models.Records {
|
||||
dedupedMap := make(map[string]bool)
|
||||
var deduped models.Records
|
||||
for _, rec := range newRecs {
|
||||
if !dedupedMap[rec.GetTargetField()] {
|
||||
dedupedMap[rec.GetTargetField()] = true
|
||||
deduped = append(deduped, rec)
|
||||
}
|
||||
}
|
||||
return deduped
|
||||
}
|
||||
|
||||
// PrepDesiredRecords munges any records to best suit this provider.
|
||||
func (a *aliDNSDsp) PrepDesiredRecords(dc *models.DomainConfig) {
|
||||
versionInfo, err := a.getDomainVersionInfo(dc.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
|
||||
|
||||
for _, rec := range dc.Records {
|
||||
// If TTL is 0 (not set), use the minimum TTL as default
|
||||
if rec.TTL == 0 {
|
||||
rec.TTL = versionInfo.minTTL
|
||||
}
|
||||
|
||||
if rec.TTL < versionInfo.minTTL {
|
||||
if rec.Type != "NS" {
|
||||
printer.Warnf("record %s has TTL %d which is below the minimum %d for this domain version (%s)\n",
|
||||
rec.GetLabelFQDN(), rec.TTL, versionInfo.minTTL, versionInfo.versionCode)
|
||||
}
|
||||
rec.TTL = versionInfo.minTTL
|
||||
}
|
||||
if rec.TTL > versionInfo.maxTTL {
|
||||
printer.Warnf("record %s has TTL %d which exceeds the maximum %d\n",
|
||||
rec.GetLabelFQDN(), rec.TTL, versionInfo.maxTTL)
|
||||
rec.TTL = versionInfo.maxTTL
|
||||
}
|
||||
recordsToKeep = append(recordsToKeep, rec)
|
||||
}
|
||||
|
||||
dc.Records = recordsToKeep
|
||||
}
|
||||
|
||||
func (a *aliDNSDsp) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) {
|
||||
// Prepare desired records first to normalize TTLs and avoid warnings
|
||||
a.PrepDesiredRecords(dc)
|
||||
|
||||
var corrections []*models.Correction
|
||||
|
||||
// Alibaba Cloud DNS is a "ByRecord" API.
|
||||
changes, actualChangeCount, err := diff2.ByRecord(existingRecords, dc, nil)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
for _, change := range changes {
|
||||
// Copy all param values to local variables to avoid overwrites
|
||||
msgs := change.MsgsJoined
|
||||
dcn := dc.Name
|
||||
chaKey := change.Key
|
||||
|
||||
if change.Type == diff2.CHANGE || change.Type == diff2.CREATE {
|
||||
if chaKey.Type == "NS" && dcn == removeTrailingDot(change.Key.NameFQDN) {
|
||||
change.New = deduplicateNameServerTargets(change.New)
|
||||
}
|
||||
}
|
||||
|
||||
switch change.Type {
|
||||
case diff2.REPORT:
|
||||
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
|
||||
case diff2.CREATE:
|
||||
changeNew := change.New
|
||||
corrections = append(corrections, &models.Correction{
|
||||
Msg: msgs,
|
||||
F: func() error {
|
||||
return a.createRecordset(changeNew, dcn)
|
||||
},
|
||||
})
|
||||
case diff2.CHANGE:
|
||||
changeNew := change.New
|
||||
changeExisting := change.Old
|
||||
corrections = append(corrections, &models.Correction{
|
||||
Msg: msgs,
|
||||
F: func() error {
|
||||
return a.updateRecordset(changeExisting, changeNew, dcn)
|
||||
},
|
||||
})
|
||||
case diff2.DELETE:
|
||||
corrections = append(corrections, &models.Correction{
|
||||
Msg: msgs,
|
||||
F: func() error {
|
||||
return a.deleteRecordset(change.Old, dcn)
|
||||
},
|
||||
})
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled change.Type %s", change.Type))
|
||||
}
|
||||
}
|
||||
|
||||
return corrections, actualChangeCount, nil
|
||||
}
|
||||
187
providers/alidns/api.go
Normal file
187
providers/alidns/api.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
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) getDomainVersionInfo(domain string) (*domainVersionInfo, error) {
|
||||
// Check cache first
|
||||
a.cacheMu.Lock()
|
||||
info, ok := a.domainVersionCache[domain]
|
||||
a.cacheMu.Unlock()
|
||||
if ok {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
req := alidns.CreateDescribeDomainInfoRequest()
|
||||
req.DomainName = domain
|
||||
|
||||
resp, err := a.client.DescribeDomainInfo(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine minTTL based on VersionCode
|
||||
var minTTL uint32
|
||||
switch resp.VersionCode {
|
||||
case "version_enterprise_advanced":
|
||||
minTTL = 1 // Enterprise Ultimate Edition
|
||||
case "version_personal", "mianfei":
|
||||
minTTL = 600 // Personal Edition and Free Edition
|
||||
default:
|
||||
// Use MinTtl from API if available, otherwise default to 600
|
||||
if resp.MinTtl > 0 {
|
||||
minTTL = uint32(resp.MinTtl)
|
||||
} else {
|
||||
minTTL = 600
|
||||
}
|
||||
}
|
||||
|
||||
info = &domainVersionInfo{
|
||||
versionCode: resp.VersionCode,
|
||||
minTTL: minTTL,
|
||||
maxTTL: 86400,
|
||||
}
|
||||
a.cacheMu.Lock()
|
||||
a.domainVersionCache[domain] = info
|
||||
a.cacheMu.Unlock()
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// GetNameservers returns the nameservers for a domain.
|
||||
func (a *aliDNSDsp) getNameservers(domain string) ([]string, error) {
|
||||
req := alidns.CreateDescribeDomainInfoRequest()
|
||||
req.DomainName = domain
|
||||
|
||||
resp, err := a.client.DescribeDomainInfo(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add trailing dot to each nameserver to make them FQDNs
|
||||
nameservers := make([]string, len(resp.DnsServers.DnsServer))
|
||||
for i, ns := range resp.DnsServers.DnsServer {
|
||||
if ns != "" && ns[len(ns)-1] != '.' {
|
||||
nameservers[i] = ns + "."
|
||||
} else {
|
||||
nameservers[i] = ns
|
||||
}
|
||||
}
|
||||
|
||||
return nameservers, nil
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
_, err := a.client.AddDomainRecord(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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.Status = "Enable"
|
||||
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, 500)
|
||||
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
|
||||
}
|
||||
|
||||
func (a *aliDNSDsp) describeDomainsAll() ([]string, error) {
|
||||
// describeDomainsAll fetches all domains in the account, handling pagination.
|
||||
fetch := func(pageNumber, pageSize int) ([]string, int, error) {
|
||||
req := alidns.CreateDescribeDomainsRequest()
|
||||
req.PageNumber = requests.NewInteger(pageNumber)
|
||||
req.PageSize = requests.NewInteger(pageSize)
|
||||
|
||||
resp, err := a.client.DescribeDomains(req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
domains := make([]string, 0, len(resp.Domains.Domain))
|
||||
for _, d := range resp.Domains.Domain {
|
||||
domains = append(domains, d.DomainName)
|
||||
}
|
||||
|
||||
total := int(resp.TotalCount)
|
||||
return domains, total, nil
|
||||
}
|
||||
|
||||
return paginateAll(fetch, 100)
|
||||
}
|
||||
64
providers/alidns/auditrecords.go
Normal file
64
providers/alidns/auditrecords.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package alidns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/rejectif"
|
||||
)
|
||||
|
||||
// isValidAliDNSString checks if a string contains only ASCII or Chinese characters.
|
||||
// Alibaba Cloud DNS allows: a-z, A-Z, 0-9, -, _, ., *, @, and Chinese characters (汉字).
|
||||
func isValidAliDNSString(s string) bool {
|
||||
for _, r := range s {
|
||||
if r > unicode.MaxASCII {
|
||||
// Allow CJK Unified Ideographs (Chinese characters): U+4E00 to U+9FFF
|
||||
// and CJK Extension A: U+3400 to U+4DBF
|
||||
if (r >= 0x4E00 && r <= 0x9FFF) || (r >= 0x3400 && r <= 0x4DBF) {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// labelConstraint detects labels that contain non-ASCII characters except Chinese characters.
|
||||
func labelConstraint(rc *models.RecordConfig) error {
|
||||
if !isValidAliDNSString(rc.GetLabel()) {
|
||||
return errors.New("label contains non-ASCII characters (only Chinese is allowed)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// targetConstraint detects target values that contain non-ASCII characters except Chinese characters.
|
||||
// This applies to CNAME, MX, NS, SRV targets.
|
||||
func targetConstraint(rc *models.RecordConfig) error {
|
||||
if !isValidAliDNSString(rc.GetTargetField()) {
|
||||
return errors.New("target contains non-ASCII characters (only Chinese is allowed)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Note: We can't get domain version info here because AuditRecords
|
||||
// is called without provider context. TTL validation will be done
|
||||
// at the provider level in GetZoneRecordsCorrections.
|
||||
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
|
||||
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified at 2025-12-03: Alibaba strips quotes
|
||||
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified at 2025-12-03: Alibaba strips trailing spaces
|
||||
a.Add("TXT", rejectif.TxtHasUnpairedBackslash) // Last verified at 2025-12-03: Alibaba mishandles odd backslashes
|
||||
a.Add("*", labelConstraint) // Last verified at 2025-12-03: Alibaba only allows ASCII + Chinese, rejects other Unicode
|
||||
a.Add("CNAME", targetConstraint) // Last verified at 2025-12-03: CNAME target must be ASCII or Chinese
|
||||
a.Add("SRV", rejectif.SrvHasNullTarget) // Last verified at 2025-12-03: SRV target must not be null
|
||||
a.Add("SRV", rejectif.SrvHasEmptyTarget) // Last verified at 2025-12-03: SRV target must not be empty
|
||||
return a.Audit(records)
|
||||
}
|
||||
110
providers/alidns/convert.go
Normal file
110
providers/alidns/convert.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
package alidns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
label, err := idna.ToASCII(r.RR)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert label to ASCII: %w", err)
|
||||
}
|
||||
rc.SetLabel(label, 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: Value contains "priority weight port target"
|
||||
// e.g., "1 1 5060 www.cloud-example.com."
|
||||
// Parse the parts and normalize the target
|
||||
parts := strings.Fields(r.Value)
|
||||
if len(parts) != 4 {
|
||||
return nil, fmt.Errorf("invalid SRV format from ALIDNS: %s", r.Value)
|
||||
}
|
||||
target := parts[3]
|
||||
// Ensure target has trailing dot for FQDN
|
||||
if target != "" && target != "." && !strings.HasSuffix(target, ".") {
|
||||
target = target + "."
|
||||
}
|
||||
// Reconstruct with normalized target and let PopulateFromString handle it
|
||||
srvValue := fmt.Sprintf("%s %s %s %s", parts[0], parts[1], parts[2], target)
|
||||
if err := rc.PopulateFromString(r.Type, srvValue, domain); err != nil {
|
||||
return nil, fmt.Errorf("unparsable SRV record received from ALIDNS: %w", err)
|
||||
}
|
||||
case "CAA":
|
||||
// Alibaba Cloud CAA format: "0 issue \"letsencrypt.org\""
|
||||
if err := rc.SetTargetCAAString(r.Value); 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 "SRV":
|
||||
return fmt.Sprintf("%d %d %d %s", r.SrvPriority, 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
|
||||
}
|
||||
}
|
||||
|
||||
// nativeToRecordNS takes a NS record from DNS and returns a native RecordConfig struct.
|
||||
func nativeToRecordNS(ns string, origin string) *models.RecordConfig {
|
||||
rc := &models.RecordConfig{
|
||||
Type: "NS",
|
||||
TTL: 600,
|
||||
}
|
||||
rc.SetLabel("@", origin)
|
||||
rc.MustSetTarget(ns)
|
||||
return rc
|
||||
}
|
||||
27
providers/alidns/pagination.go
Normal file
27
providers/alidns/pagination.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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), maxPageSize int) ([]T, error) {
|
||||
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
|
||||
}
|
||||
|
|
@ -83,6 +83,20 @@ func RegisterMaintainer(
|
|||
ProviderMaintainers[providerName] = gitHubUsername
|
||||
}
|
||||
|
||||
// ProviderDefaultTTLs stores the default TTL for each provider.
|
||||
var ProviderDefaultTTLs = map[string]uint32{}
|
||||
|
||||
// RegisterDefaultTTL registers a default TTL for a provider.
|
||||
// This is used by get-zones to determine the DefaultTTL when generating output.
|
||||
func RegisterDefaultTTL(providerName string, defaultTTL uint32) {
|
||||
ProviderDefaultTTLs[providerName] = defaultTTL
|
||||
}
|
||||
|
||||
// GetDefaultTTL returns the default TTL for a provider, or 0 if not registered.
|
||||
func GetDefaultTTL(providerName string) uint32 {
|
||||
return ProviderDefaultTTLs[providerName]
|
||||
}
|
||||
|
||||
// CreateRegistrar initializes a registrar instance from given credentials.
|
||||
func CreateRegistrar(rType string, config map[string]string) (Registrar, error) {
|
||||
var err error
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue