mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
5
.github/workflows/pr_test.yml
vendored
5
.github/workflows/pr_test.yml
vendored
@ -87,7 +87,7 @@ jobs:
|
|||||||
Write-Host "Integration test providers: $Providers"
|
Write-Host "Integration test providers: $Providers"
|
||||||
echo "integration_test_providers=$(ConvertTo-Json -InputObject $Providers -Compress)" >> $env:GITHUB_OUTPUT
|
echo "integration_test_providers=$(ConvertTo-Json -InputObject $Providers -Compress)" >> $env:GITHUB_OUTPUT
|
||||||
env:
|
env:
|
||||||
PROVIDERS: "['AZURE_DNS','BIND','CLOUDFLAREAPI','CLOUDNS','DIGITALOCEAN','GANDI_V5','GCLOUD','HEDNS','HEXONET','INWX','NAMEDOTCOM','NS1','POWERDNS','ROUTE53','TRANSIP']"
|
PROVIDERS: "['AZURE_DNS','BIND','BUNNY_DNS','CLOUDFLAREAPI','CLOUDNS','DIGITALOCEAN','GANDI_V5','GCLOUD','HEDNS','HEXONET','INWX','NAMEDOTCOM','NS1','POWERDNS','ROUTE53','TRANSIP']"
|
||||||
ENV_CONTEXT: ${{ toJson(env) }}
|
ENV_CONTEXT: ${{ toJson(env) }}
|
||||||
VARS_CONTEXT: ${{ toJson(vars) }}
|
VARS_CONTEXT: ${{ toJson(vars) }}
|
||||||
SECRETS_CONTEXT: ${{ toJson(secrets) }}
|
SECRETS_CONTEXT: ${{ toJson(secrets) }}
|
||||||
@ -106,6 +106,7 @@ jobs:
|
|||||||
# Set it to the domain name to use during the test.
|
# Set it to the domain name to use during the test.
|
||||||
AZURE_DNS_DOMAIN: ${{ vars.AZURE_DNS_DOMAIN }}
|
AZURE_DNS_DOMAIN: ${{ vars.AZURE_DNS_DOMAIN }}
|
||||||
BIND_DOMAIN: ${{ vars.BIND_DOMAIN }}
|
BIND_DOMAIN: ${{ vars.BIND_DOMAIN }}
|
||||||
|
BUNNY_DNS_DOMAIN: ${{ vars.BUNNY_DNS_DOMAIN }}
|
||||||
CLOUDFLAREAPI_DOMAIN: ${{ vars.CLOUDFLAREAPI_DOMAIN }}
|
CLOUDFLAREAPI_DOMAIN: ${{ vars.CLOUDFLAREAPI_DOMAIN }}
|
||||||
CLOUDNS_DOMAIN: ${{ vars.CLOUDNS_DOMAIN }}
|
CLOUDNS_DOMAIN: ${{ vars.CLOUDNS_DOMAIN }}
|
||||||
CSCGLOBAL_DOMAIN: ${{ vars.CSCGLOBAL_DOMAIN }}
|
CSCGLOBAL_DOMAIN: ${{ vars.CSCGLOBAL_DOMAIN }}
|
||||||
@ -129,6 +130,8 @@ jobs:
|
|||||||
AZURE_DNS_SUBSCRIPTION_ID: ${{ secrets.AZURE_DNS_SUBSCRIPTION_ID }}
|
AZURE_DNS_SUBSCRIPTION_ID: ${{ secrets.AZURE_DNS_SUBSCRIPTION_ID }}
|
||||||
AZURE_DNS_TENANT_ID: ${{ secrets.AZURE_DNS_TENANT_ID }}
|
AZURE_DNS_TENANT_ID: ${{ secrets.AZURE_DNS_TENANT_ID }}
|
||||||
|
|
||||||
|
BUNNY_DNS_API_KEY: ${{ secrets.BUNNY_DNS_API_KEY }}
|
||||||
|
|
||||||
CLOUDFLAREAPI_ACCOUNTID: ${{ secrets.CLOUDFLAREAPI_ACCOUNTID }}
|
CLOUDFLAREAPI_ACCOUNTID: ${{ secrets.CLOUDFLAREAPI_ACCOUNTID }}
|
||||||
CLOUDFLAREAPI_TOKEN: ${{ secrets.CLOUDFLAREAPI_TOKEN }}
|
CLOUDFLAREAPI_TOKEN: ${{ secrets.CLOUDFLAREAPI_TOKEN }}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ changelog:
|
|||||||
regexp: "(?i)^.*(major|new provider|feature)[(\\w)]*:+.*$"
|
regexp: "(?i)^.*(major|new provider|feature)[(\\w)]*:+.*$"
|
||||||
order: 1
|
order: 1
|
||||||
- title: 'Provider-specific changes:'
|
- title: 'Provider-specific changes:'
|
||||||
regexp: "(?i)((akamaiedge|autodns|axfrd|azure|azure_private_dns|bind|cloudflare|cloudflareapi_old|cloudns|cscglobal|desec|digitalocean|dnsimple|dnsmadeeasy|doh|domainnameshop|dynadot|easyname|exoscale|gandi|gcloud|gcore|hedns|hetzner|hexonet|hostingde|inwx|linode|loopia|luadns|msdns|mythicbeasts|namecheap|namedotcom|netcup|netlify|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|route53|rwth|softlayer|transip|vultr).*:)+.*"
|
regexp: "(?i)((akamaiedge|autodns|axfrd|azure|azure_private_dns|bind|bunnydns|cloudflare|cloudflareapi_old|cloudns|cscglobal|desec|digitalocean|dnsimple|dnsmadeeasy|doh|domainnameshop|dynadot|easyname|exoscale|gandi|gcloud|gcore|hedns|hetzner|hexonet|hostingde|inwx|linode|loopia|luadns|msdns|mythicbeasts|namecheap|namedotcom|netcup|netlify|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|route53|rwth|softlayer|transip|vultr).*:)+.*"
|
||||||
order: 2
|
order: 2
|
||||||
- title: 'Documentation:'
|
- title: 'Documentation:'
|
||||||
regexp: "(?i)^.*(docs)[(\\w)]*:+.*$"
|
regexp: "(?i)^.*(docs)[(\\w)]*:+.*$"
|
||||||
|
1
OWNERS
1
OWNERS
@ -4,6 +4,7 @@ providers/axfrddns @hnrgrgr
|
|||||||
providers/azuredns @vatsalyagoel
|
providers/azuredns @vatsalyagoel
|
||||||
providers/azureprivatedns @matthewmgamble
|
providers/azureprivatedns @matthewmgamble
|
||||||
providers/bind @tlimoncelli
|
providers/bind @tlimoncelli
|
||||||
|
providers/bunnydns @ppmathis
|
||||||
providers/cloudflare @tresni
|
providers/cloudflare @tresni
|
||||||
providers/cloudns @pragmaton
|
providers/cloudns @pragmaton
|
||||||
providers/cscglobal @mikenz
|
providers/cscglobal @mikenz
|
||||||
|
@ -23,6 +23,7 @@ Currently supported DNS providers:
|
|||||||
- Azure DNS
|
- Azure DNS
|
||||||
- Azure Private DNS
|
- Azure Private DNS
|
||||||
- BIND
|
- BIND
|
||||||
|
- Bunny DNS
|
||||||
- Cloudflare
|
- Cloudflare
|
||||||
- ClouDNS
|
- ClouDNS
|
||||||
- deSEC
|
- deSEC
|
||||||
|
@ -104,6 +104,7 @@
|
|||||||
* [Azure DNS](providers/azure_dns.md)
|
* [Azure DNS](providers/azure_dns.md)
|
||||||
* [Azure Private DNS](providers/azure_private_dns.md)
|
* [Azure Private DNS](providers/azure_private_dns.md)
|
||||||
* [BIND](providers/bind.md)
|
* [BIND](providers/bind.md)
|
||||||
|
* [Bunny DNS](providers/bunny_dns.md)
|
||||||
* [Cloudflare](providers/cloudflareapi.md)
|
* [Cloudflare](providers/cloudflareapi.md)
|
||||||
* [ClouDNS](providers/cloudns.md)
|
* [ClouDNS](providers/cloudns.md)
|
||||||
* [CSC Global](providers/cscglobal.md)
|
* [CSC Global](providers/cscglobal.md)
|
||||||
|
@ -20,6 +20,7 @@ If a feature is definitively not supported for whatever reason, we would also li
|
|||||||
| [`AZURE_DNS`](providers/azure_dns.md) | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
| [`AZURE_DNS`](providers/azure_dns.md) | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [`AZURE_PRIVATE_DNS`](providers/azure_private_dns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
| [`AZURE_PRIVATE_DNS`](providers/azure_private_dns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [`BIND`](providers/bind.md) | ✅ | ✅ | ❌ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
| [`BIND`](providers/bind.md) | ✅ | ✅ | ❌ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||||
|
| [`BUNNY_DNS`](providers/bunny_dns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
|
||||||
| [`CLOUDFLAREAPI`](providers/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❌ | ✅ | ✅ | ✅ |
|
| [`CLOUDFLAREAPI`](providers/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❌ | ✅ | ✅ | ✅ |
|
||||||
| [`CLOUDNS`](providers/cloudns.md) | ❌ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ |
|
| [`CLOUDNS`](providers/cloudns.md) | ❌ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ |
|
||||||
| [`CSCGLOBAL`](providers/cscglobal.md) | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | ✅ |
|
| [`CSCGLOBAL`](providers/cscglobal.md) | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | ✅ |
|
||||||
@ -110,6 +111,7 @@ Providers in this category and their maintainers are:
|
|||||||
|[`AZURE_PRIVATE_DNS`](providers/azure_private_dns.md)|@matthewmgamble|
|
|[`AZURE_PRIVATE_DNS`](providers/azure_private_dns.md)|@matthewmgamble|
|
||||||
|[`AKAMAIEDGEDNS`](providers/akamaiedgedns.md)|@svernick|
|
|[`AKAMAIEDGEDNS`](providers/akamaiedgedns.md)|@svernick|
|
||||||
|[`AXFRDDNS`](providers/axfrddns.md)|@hnrgrgr|
|
|[`AXFRDDNS`](providers/axfrddns.md)|@hnrgrgr|
|
||||||
|
|[`BUNNY_DNS`](providers/bunny_dns.md)|@ppmathis|
|
||||||
|[`CLOUDFLAREAPI`](providers/cloudflareapi.md)|@tresni|
|
|[`CLOUDFLAREAPI`](providers/cloudflareapi.md)|@tresni|
|
||||||
|[`CLOUDNS`](providers/cloudns.md)|@pragmaton|
|
|[`CLOUDNS`](providers/cloudns.md)|@pragmaton|
|
||||||
|[`CSCGLOBAL`](providers/cscglobal.md)|@Air-New-Zealand|
|
|[`CSCGLOBAL`](providers/cscglobal.md)|@Air-New-Zealand|
|
||||||
@ -154,7 +156,6 @@ code to support this provider, we'd be glad to help in any way.
|
|||||||
|
|
||||||
* [1984 Hosting](https://github.com/StackExchange/dnscontrol/issues/1251) (#1251)
|
* [1984 Hosting](https://github.com/StackExchange/dnscontrol/issues/1251) (#1251)
|
||||||
* [Alibaba Cloud DNS](https://github.com/StackExchange/dnscontrol/issues/420)(#420)
|
* [Alibaba Cloud DNS](https://github.com/StackExchange/dnscontrol/issues/420)(#420)
|
||||||
* [BunnyDNS](https://github.com/StackExchange/dnscontrol/issues/2265)(#2265)
|
|
||||||
* [Constellix (DNSMadeEasy)](https://github.com/StackExchange/dnscontrol/issues/842) (#842)
|
* [Constellix (DNSMadeEasy)](https://github.com/StackExchange/dnscontrol/issues/842) (#842)
|
||||||
* [CoreDNS](https://github.com/StackExchange/dnscontrol/issues/1284) (#1284)
|
* [CoreDNS](https://github.com/StackExchange/dnscontrol/issues/1284) (#1284)
|
||||||
* [EU.ORG](https://github.com/StackExchange/dnscontrol/issues/1176) (#1176)
|
* [EU.ORG](https://github.com/StackExchange/dnscontrol/issues/1176) (#1176)
|
||||||
|
69
documentation/providers/bunny_dns.md
Normal file
69
documentation/providers/bunny_dns.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
To use this provider, add an entry to `creds.json` with `TYPE` set to `BUNNY_DNS` along with
|
||||||
|
your [Bunny API Key](https://dash.bunny.net/account/settings).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{% code title="creds.json" %}
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bunny_dns": {
|
||||||
|
"TYPE": "BUNNY_DNS",
|
||||||
|
"api_key": "your-bunny-api-key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
You can also use environment variables:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export BUNNY_API_KEY=XXXXXXXXX
|
||||||
|
```
|
||||||
|
|
||||||
|
{% code title="creds.json" %}
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bunny_dns": {
|
||||||
|
"TYPE": "BUNNY_DNS",
|
||||||
|
"api_key": "$BUNNY_API_KEY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
This provider does not recognize any special metadata fields unique to Bunny DNS.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
An example configuration:
|
||||||
|
|
||||||
|
{% code title="dnsconfig.js" %}
|
||||||
|
```javascript
|
||||||
|
var REG_NONE = NewRegistrar("none");
|
||||||
|
var DSP_BUNNY_DNS = NewDnsProvider("bunny_dns");
|
||||||
|
|
||||||
|
D("example.com", REG_NONE, DnsProvider(DSP_BUNNY_DNS),
|
||||||
|
A("test", "1.2.3.4")
|
||||||
|
);
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
# Activation
|
||||||
|
|
||||||
|
DNSControl depends on the [Bunny API](https://docs.bunny.net/reference/bunnynet-api-overview) to manage your DNS
|
||||||
|
records. You will need to generate an [API key](https://dash.bunny.net/account/settings) to use this provider.
|
||||||
|
|
||||||
|
## New domains
|
||||||
|
|
||||||
|
If a domain does not exist in your Bunny account, DNSControl will automatically add it with the `push` command.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
- Bunny DNS does not support dual-hosting or configuring custom TTLs for NS records on the zone apex.
|
||||||
|
- While custom nameservers are properly recognized by this provider, it is currently not possible to configure them.
|
||||||
|
- Any custom record types like Script, Redirect, Flatten or Pull Zone are currently not supported by this provider. Such
|
||||||
|
records will be completely ignored by DNSControl and left as-is.
|
@ -38,6 +38,11 @@
|
|||||||
"TYPE": "BIND",
|
"TYPE": "BIND",
|
||||||
"domain": "$BIND_DOMAIN"
|
"domain": "$BIND_DOMAIN"
|
||||||
},
|
},
|
||||||
|
"BUNNY_DNS": {
|
||||||
|
"TYPE": "BUNNY_DNS",
|
||||||
|
"domain": "$BUNNY_DNS_DOMAIN",
|
||||||
|
"api_key": "$BUNNY_DNS_API_KEY"
|
||||||
|
},
|
||||||
"CLOUDFLAREAPI": {
|
"CLOUDFLAREAPI": {
|
||||||
"TYPE": "CLOUDFLAREAPI",
|
"TYPE": "CLOUDFLAREAPI",
|
||||||
"accountid": "$CLOUDFLAREAPI_ACCOUNTID",
|
"accountid": "$CLOUDFLAREAPI_ACCOUNTID",
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/azuredns"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/azuredns"
|
||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/azureprivatedns"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/azureprivatedns"
|
||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/bind"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/bind"
|
||||||
|
_ "github.com/StackExchange/dnscontrol/v4/providers/bunnydns"
|
||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/cloudflare"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/cloudflare"
|
||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/cloudns"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/cloudns"
|
||||||
_ "github.com/StackExchange/dnscontrol/v4/providers/cscglobal"
|
_ "github.com/StackExchange/dnscontrol/v4/providers/cscglobal"
|
||||||
|
227
providers/bunnydns/api.go
Normal file
227
providers/bunnydns/api.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
baseURL = "https://api.bunny.net"
|
||||||
|
pageSize = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
type zone struct {
|
||||||
|
ID int64 `json:"Id"`
|
||||||
|
Domain string `json:"Domain"`
|
||||||
|
Nameserver1 string `json:"Nameserver1"`
|
||||||
|
Nameserver2 string `json:"Nameserver2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (zone *zone) Nameservers() []string {
|
||||||
|
return []string{zone.Nameserver1, zone.Nameserver2}
|
||||||
|
}
|
||||||
|
|
||||||
|
type record struct {
|
||||||
|
ID int64 `json:"Id,omitempty"`
|
||||||
|
Type recordType `json:"Type"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
Disabled bool `json:"Disabled"`
|
||||||
|
TTL uint32 `json:"Ttl"`
|
||||||
|
Flags uint8 `json:"Flags"`
|
||||||
|
Priority uint16 `json:"Priority"`
|
||||||
|
Weight uint16 `json:"Weight"`
|
||||||
|
Port uint16 `json:"Port"`
|
||||||
|
Tag string `json:"Tag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type listZonesResponse struct {
|
||||||
|
Items []zone `json:"Items"`
|
||||||
|
TotalItems int32 `json:"TotalItems"`
|
||||||
|
HasMoreItems bool `json:"HasMoreItems"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type getZoneResponse struct {
|
||||||
|
zone
|
||||||
|
Records []record `json:"Records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryParams map[string]string
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) getImplicitRecordConfigs(zone *zone) (models.Records, error) {
|
||||||
|
nameservers := zone.Nameservers()
|
||||||
|
records := make(models.Records, 0, len(nameservers))
|
||||||
|
|
||||||
|
// NS records on the zone apex must be implicitly added, as Bunny DNS does not expose them via API
|
||||||
|
for _, ns := range nameservers {
|
||||||
|
rc := &models.RecordConfig{
|
||||||
|
Type: "NS",
|
||||||
|
Original: &record{},
|
||||||
|
}
|
||||||
|
rc.SetLabelFromFQDN(zone.Domain, zone.Domain)
|
||||||
|
if err := rc.SetTarget(ns + "."); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records = append(records, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) findZoneByDomain(domain string) (*zone, error) {
|
||||||
|
if b.zones == nil {
|
||||||
|
zones, err := b.getAllZones()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.zones = make(map[string]*zone, len(zones))
|
||||||
|
for _, zone := range zones {
|
||||||
|
b.zones[zone.Domain] = zone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, ok := b.zones[domain]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%q is not a zone in this BUNNY_DNS account", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) getAllZones() ([]*zone, error) {
|
||||||
|
var zones []*zone
|
||||||
|
page := 1
|
||||||
|
|
||||||
|
for {
|
||||||
|
res := listZonesResponse{}
|
||||||
|
query := queryParams{"page": strconv.Itoa(page), "perPage": strconv.Itoa(pageSize)}
|
||||||
|
if err := b.request("GET", "/dnszone", query, nil, &res, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not fetch zones: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if zones == nil {
|
||||||
|
zones = make([]*zone, 0, res.TotalItems)
|
||||||
|
}
|
||||||
|
for i := range res.Items {
|
||||||
|
zones = append(zones, &res.Items[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.HasMoreItems {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
|
||||||
|
return zones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) createZone(domain string) (*zone, error) {
|
||||||
|
zone := &zone{}
|
||||||
|
body := map[string]string{"domain": domain}
|
||||||
|
err := b.request("POST", "/dnszone", nil, body, &zone, []int{http.StatusCreated})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.zones[domain] = zone
|
||||||
|
return zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) getAllRecords(zoneID int64) ([]*record, error) {
|
||||||
|
zone := &getZoneResponse{}
|
||||||
|
err := b.request("GET", fmt.Sprintf("/dnszone/%d", zoneID), nil, nil, zone, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]*record, 0, len(zone.Records))
|
||||||
|
for i := range zone.Records {
|
||||||
|
records = append(records, &zone.Records[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) createRecord(zoneID int64, r *record) error {
|
||||||
|
url := fmt.Sprintf("/dnszone/%d/records", zoneID)
|
||||||
|
return b.request("PUT", url, nil, r, nil, []int{http.StatusCreated})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) modifyRecord(zoneID int64, recordID int64, r *record) error {
|
||||||
|
url := fmt.Sprintf("/dnszone/%d/records/%d", zoneID, recordID)
|
||||||
|
return b.request("POST", url, nil, r, nil, []int{http.StatusNoContent})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) deleteRecord(zoneID, recordID int64) error {
|
||||||
|
url := fmt.Sprintf("/dnszone/%d/records/%d", zoneID, recordID)
|
||||||
|
return b.request("DELETE", url, nil, nil, nil, []int{http.StatusNoContent})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) request(method, endpoint string, query queryParams, body, target any, validStatus []int) error {
|
||||||
|
if validStatus == nil {
|
||||||
|
validStatus = []int{http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestBody io.Reader
|
||||||
|
if body != nil {
|
||||||
|
requestBodyJSON, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
requestBody = bytes.NewBuffer(requestBodyJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, baseURL+endpoint, requestBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("AccessKey", b.apiKey)
|
||||||
|
if requestBody != nil {
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
if query != nil {
|
||||||
|
q := req.URL.Query()
|
||||||
|
for k, v := range query {
|
||||||
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cleanup := func() {
|
||||||
|
if err := resp.Body.Close(); err != nil {
|
||||||
|
printer.Printf("BUNNY_DNS: Could not close response body after API call: %q\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(validStatus, resp.StatusCode) {
|
||||||
|
data, _ := io.ReadAll(resp.Body)
|
||||||
|
printer.Println(fmt.Sprintf("BUNNY_DNS: Bad API response for %s %s: %s", method, endpoint, string(data)))
|
||||||
|
cleanup()
|
||||||
|
return fmt.Errorf("bad status code from BUNNY_DNS: %d not in %v", resp.StatusCode, validStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == nil {
|
||||||
|
cleanup()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(target)
|
||||||
|
cleanup()
|
||||||
|
return err
|
||||||
|
}
|
17
providers/bunnydns/auditrecords.go
Normal file
17
providers/bunnydns/auditrecords.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
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("TXT", rejectif.TxtIsEmpty) // Last verified 2024-01-02
|
||||||
|
a.Add("SRV", rejectif.SrvHasNullTarget) // Last verified 2024-01-02
|
||||||
|
|
||||||
|
return a.Audit(records)
|
||||||
|
}
|
61
providers/bunnydns/bunnydnsProvider.go
Normal file
61
providers/bunnydns/bunnydnsProvider.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/providers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var features = providers.DocumentationNotes{
|
||||||
|
providers.CanAutoDNSSEC: providers.Cannot(),
|
||||||
|
providers.CanGetZones: providers.Can(),
|
||||||
|
providers.CanUseAlias: providers.Cannot(),
|
||||||
|
providers.CanUseCAA: providers.Can(),
|
||||||
|
providers.CanUseDHCID: providers.Cannot(),
|
||||||
|
providers.CanUseDS: providers.Cannot(),
|
||||||
|
providers.CanUseDSForChildren: providers.Cannot(),
|
||||||
|
providers.CanUseLOC: providers.Cannot(),
|
||||||
|
providers.CanUseNAPTR: providers.Cannot(),
|
||||||
|
providers.CanUsePTR: providers.Can(),
|
||||||
|
providers.CanUseSOA: providers.Cannot(),
|
||||||
|
providers.CanUseSRV: providers.Can(),
|
||||||
|
providers.CanUseSSHFP: providers.Cannot(),
|
||||||
|
providers.CanUseTLSA: providers.Cannot(),
|
||||||
|
providers.DocCreateDomains: providers.Can(),
|
||||||
|
providers.DocDualHost: providers.Cannot(),
|
||||||
|
providers.DocOfficiallySupported: providers.Cannot(),
|
||||||
|
}
|
||||||
|
|
||||||
|
type bunnydnsProvider struct {
|
||||||
|
apiKey string
|
||||||
|
zones map[string]*zone
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
fns := providers.DspFuncs{
|
||||||
|
Initializer: newBunnydns,
|
||||||
|
RecordAuditor: AuditRecords,
|
||||||
|
}
|
||||||
|
providers.RegisterDomainServiceProviderType("BUNNY_DNS", fns, features)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBunnydns(settings map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||||
|
apiKey := settings["api_key"]
|
||||||
|
if apiKey == "" {
|
||||||
|
return nil, fmt.Errorf("missing BUNNY_DNS api_key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bunnydnsProvider{
|
||||||
|
apiKey: apiKey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||||
|
zone, err := b.findZoneByDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.ToNameservers(zone.Nameservers())
|
||||||
|
}
|
161
providers/bunnydns/convert.go
Normal file
161
providers/bunnydns/convert.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/models"
|
||||||
|
"github.com/miekg/dns/dnsutil"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fqdnTypes = []recordType{recordTypeCNAME, recordTypeMX, recordTypeNS, recordTypePTR, recordTypeSRV}
|
||||||
|
|
||||||
|
func fromRecordConfig(rc *models.RecordConfig) (*record, error) {
|
||||||
|
r := record{
|
||||||
|
Type: recordTypeFromString(rc.Type),
|
||||||
|
Name: rc.GetLabel(),
|
||||||
|
Value: rc.GetTargetField(),
|
||||||
|
TTL: rc.TTL,
|
||||||
|
}
|
||||||
|
|
||||||
|
// While Bunny DNS does not use trailing dots, it still accepts and even preserves them for certain record types.
|
||||||
|
// To avoid confusion, any trailing dots are removed from the record value.
|
||||||
|
if slices.Contains(fqdnTypes, r.Type) && strings.HasSuffix(r.Value, ".") {
|
||||||
|
r.Value = strings.TrimSuffix(r.Value, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.Type {
|
||||||
|
case recordTypeNS:
|
||||||
|
if r.Name == "" {
|
||||||
|
r.TTL = 0
|
||||||
|
}
|
||||||
|
case recordTypeSRV:
|
||||||
|
r.Priority = rc.SrvPriority
|
||||||
|
r.Weight = rc.SrvWeight
|
||||||
|
r.Port = rc.SrvPort
|
||||||
|
case recordTypeCAA:
|
||||||
|
r.Flags = rc.CaaFlag
|
||||||
|
r.Tag = rc.CaaTag
|
||||||
|
case recordTypeMX:
|
||||||
|
r.Priority = rc.MxPreference
|
||||||
|
}
|
||||||
|
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toRecordConfig(domain string, r *record) (*models.RecordConfig, error) {
|
||||||
|
rc := models.RecordConfig{
|
||||||
|
Type: recordTypeToString(r.Type),
|
||||||
|
TTL: r.TTL,
|
||||||
|
Original: r,
|
||||||
|
}
|
||||||
|
rc.SetLabel(r.Name, domain)
|
||||||
|
|
||||||
|
// Bunny DNS always operates with fully-qualified names and does not use any trailing dots.
|
||||||
|
// If a record already contains a trailing dot, which the provider UI also accepts, the record value is left as-is.
|
||||||
|
recordValue := r.Value
|
||||||
|
if slices.Contains(fqdnTypes, r.Type) && !strings.HasSuffix(r.Value, ".") {
|
||||||
|
recordValue = dnsutil.AddOrigin(r.Value+".", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch rc.Type {
|
||||||
|
case "CAA":
|
||||||
|
err = rc.SetTargetCAA(r.Flags, r.Tag, recordValue)
|
||||||
|
case "MX":
|
||||||
|
err = rc.SetTargetMX(r.Priority, recordValue)
|
||||||
|
case "SRV":
|
||||||
|
err = rc.SetTargetSRV(r.Priority, r.Weight, r.Port, recordValue)
|
||||||
|
default:
|
||||||
|
err = rc.PopulateFromStringFunc(rc.Type, recordValue, domain, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type recordType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
recordTypeA recordType = 0
|
||||||
|
recordTypeAAAA recordType = 1
|
||||||
|
recordTypeCNAME recordType = 2
|
||||||
|
recordTypeTXT recordType = 3
|
||||||
|
recordTypeMX recordType = 4
|
||||||
|
recordTypeRedirect recordType = 5
|
||||||
|
recordTypeFlatten recordType = 6
|
||||||
|
recordTypePullZone recordType = 7
|
||||||
|
recordTypeSRV recordType = 8
|
||||||
|
recordTypeCAA recordType = 9
|
||||||
|
recordTypePTR recordType = 10
|
||||||
|
recordTypeScript recordType = 11
|
||||||
|
recordTypeNS recordType = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
func recordTypeFromString(t string) recordType {
|
||||||
|
switch t {
|
||||||
|
case "A":
|
||||||
|
return recordTypeA
|
||||||
|
case "AAAA":
|
||||||
|
return recordTypeAAAA
|
||||||
|
case "CNAME":
|
||||||
|
return recordTypeCNAME
|
||||||
|
case "TXT":
|
||||||
|
return recordTypeTXT
|
||||||
|
case "MX":
|
||||||
|
return recordTypeMX
|
||||||
|
case "REDIRECT":
|
||||||
|
return recordTypeRedirect
|
||||||
|
case "FLATTEN":
|
||||||
|
return recordTypeFlatten
|
||||||
|
case "PULL_ZONE":
|
||||||
|
return recordTypePullZone
|
||||||
|
case "SRV":
|
||||||
|
return recordTypeSRV
|
||||||
|
case "CAA":
|
||||||
|
return recordTypeCAA
|
||||||
|
case "PTR":
|
||||||
|
return recordTypePTR
|
||||||
|
case "SCRIPT":
|
||||||
|
return recordTypeScript
|
||||||
|
case "NS":
|
||||||
|
return recordTypeNS
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("BUNNY_DNS: rtype %v unimplemented", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recordTypeToString(t recordType) string {
|
||||||
|
switch t {
|
||||||
|
case recordTypeA:
|
||||||
|
return "A"
|
||||||
|
case recordTypeAAAA:
|
||||||
|
return "AAAA"
|
||||||
|
case recordTypeCNAME:
|
||||||
|
return "CNAME"
|
||||||
|
case recordTypeTXT:
|
||||||
|
return "TXT"
|
||||||
|
case recordTypeMX:
|
||||||
|
return "MX"
|
||||||
|
case recordTypeRedirect:
|
||||||
|
return "REDIRECT"
|
||||||
|
case recordTypeFlatten:
|
||||||
|
return "FLATTEN"
|
||||||
|
case recordTypePullZone:
|
||||||
|
return "PULL_ZONE"
|
||||||
|
case recordTypeSRV:
|
||||||
|
return "SRV"
|
||||||
|
case recordTypeCAA:
|
||||||
|
return "CAA"
|
||||||
|
case recordTypePTR:
|
||||||
|
return "PTR"
|
||||||
|
case recordTypeScript:
|
||||||
|
return "SCRIPT"
|
||||||
|
case recordTypeNS:
|
||||||
|
return "NS"
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("BUNNY_DNS: native rtype %v unimplemented", t))
|
||||||
|
}
|
||||||
|
}
|
32
providers/bunnydns/listzones.go
Normal file
32
providers/bunnydns/listzones.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
import "github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) ListZones() ([]string, error) {
|
||||||
|
zones, err := b.getAllZones()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneNames := make([]string, 0, len(zones))
|
||||||
|
for _, zone := range zones {
|
||||||
|
zoneNames = append(zoneNames, zone.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return zoneNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) EnsureZoneExists(domain string) error {
|
||||||
|
_, err := b.findZoneByDomain(domain)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := b.createZone(domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.Warnf("BUNNY_DNS: Added zone %s with ID %d", domain, zone.ID)
|
||||||
|
return nil
|
||||||
|
}
|
147
providers/bunnydns/records.go
Normal file
147
providers/bunnydns/records.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package bunnydns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
|
||||||
|
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
|
||||||
|
zone, err := b.findZoneByDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeRecs, err := b.getAllRecords(zone.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitRecs, err := b.getImplicitRecordConfigs(zone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
recs := make(models.Records, 0, len(nativeRecs)+len(implicitRecs))
|
||||||
|
recs = append(recs, implicitRecs...)
|
||||||
|
|
||||||
|
// Define a list of record types that are currently not supported by this provider.
|
||||||
|
unsupportedTypes := []recordType{
|
||||||
|
recordTypeRedirect,
|
||||||
|
recordTypeFlatten,
|
||||||
|
recordTypePullZone,
|
||||||
|
recordTypeScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all native records and convert them to standardized RecordConfigs
|
||||||
|
// Unsupported record types are ignored with a warning and will remain untouched in the zone.
|
||||||
|
for _, nativeRec := range nativeRecs {
|
||||||
|
if slices.Contains(unsupportedTypes, nativeRec.Type) {
|
||||||
|
printer.Warnf("BUNNY_DNS: ignoring unsupported record type %s\n", recordTypeToString(nativeRec.Type))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rc, err := toRecordConfig(zone.Domain, nativeRec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
recs = append(recs, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return recs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
|
||||||
|
// Bunny DNS never returns NS records for the apex domain, so these are artificially added when retrieving records.
|
||||||
|
// As no TTL can be configured or retrieved for these NS records, we set it to 0 to avoid unnecessary updates.
|
||||||
|
for _, rc := range dc.Records {
|
||||||
|
if rc.Name == "@" && rc.Type == "NS" {
|
||||||
|
rc.TTL = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := b.findZoneByDomain(dc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions, err := diff2.ByRecord(existing, dc, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var corrections []*models.Correction
|
||||||
|
for _, inst := range instructions {
|
||||||
|
switch inst.Type {
|
||||||
|
case diff2.REPORT:
|
||||||
|
corrections = append(corrections, &models.Correction{
|
||||||
|
Msg: inst.MsgsJoined,
|
||||||
|
})
|
||||||
|
case diff2.CREATE:
|
||||||
|
corrections = append(corrections, b.mkCreateCorrection(
|
||||||
|
zone.ID, inst.New[0], inst.Msgs[0],
|
||||||
|
))
|
||||||
|
case diff2.CHANGE:
|
||||||
|
corrections = append(corrections, b.mkChangeCorrection(
|
||||||
|
zone.ID, inst.Old[0], inst.New[0], inst.Msgs[0],
|
||||||
|
))
|
||||||
|
case diff2.DELETE:
|
||||||
|
corrections = append(corrections, b.mkDeleteCorrection(
|
||||||
|
zone.ID, inst.Old[0], inst.Msgs[0],
|
||||||
|
))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled inst.Type %s", inst.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return corrections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) mkCreateCorrection(zoneID int64, newRec *models.RecordConfig, msg string) *models.Correction {
|
||||||
|
return &models.Correction{
|
||||||
|
Msg: msg,
|
||||||
|
F: func() error {
|
||||||
|
desired, err := fromRecordConfig(newRec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.createRecord(zoneID, desired)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) mkChangeCorrection(zoneID int64, oldRec, newRec *models.RecordConfig, msg string) *models.Correction {
|
||||||
|
return &models.Correction{
|
||||||
|
Msg: msg,
|
||||||
|
F: func() error {
|
||||||
|
existingID := oldRec.Original.(*record).ID
|
||||||
|
if existingID == 0 {
|
||||||
|
return fmt.Errorf("BUNNY_DNS: cannot change implicit records")
|
||||||
|
}
|
||||||
|
|
||||||
|
desired, err := fromRecordConfig(newRec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.modifyRecord(zoneID, existingID, desired)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bunnydnsProvider) mkDeleteCorrection(zoneID int64, oldRec *models.RecordConfig, msg string) *models.Correction {
|
||||||
|
return &models.Correction{
|
||||||
|
Msg: msg,
|
||||||
|
F: func() error {
|
||||||
|
existingID := oldRec.Original.(*record).ID
|
||||||
|
if existingID == 0 {
|
||||||
|
return fmt.Errorf("BUNNY_DNS: cannot delete implicit records")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.deleteRecord(zoneID, existingID)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user