mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-11-10 17:26:10 +08:00
a00572af4a
* preview/push: ensure zones exists before querying them Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * HETZNER: reset zone cache when creating a new zone Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * ROUTE53: reset zone cache when creating a new zone Signed-off-by: Jakob Ackermann <das7pad@outlook.com> Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
203 lines
5.3 KiB
Go
203 lines
5.3 KiB
Go
package hetzner
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/StackExchange/dnscontrol/v3/models"
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
|
|
"github.com/StackExchange/dnscontrol/v3/providers"
|
|
)
|
|
|
|
var features = providers.DocumentationNotes{
|
|
providers.CanGetZones: providers.Can(),
|
|
providers.CanUseAlias: providers.Cannot(),
|
|
providers.CanUseCAA: providers.Can(),
|
|
providers.CanUseDS: providers.Cannot(),
|
|
providers.CanUsePTR: providers.Cannot(),
|
|
providers.CanUseSRV: providers.Can(),
|
|
providers.CanUseSSHFP: providers.Cannot(),
|
|
providers.CanUseTLSA: providers.Cannot(),
|
|
providers.DocCreateDomains: providers.Can(),
|
|
providers.DocDualHost: providers.Can(),
|
|
providers.DocOfficiallySupported: providers.Cannot(),
|
|
}
|
|
|
|
func init() {
|
|
fns := providers.DspFuncs{
|
|
Initializer: New,
|
|
RecordAuditor: AuditRecords,
|
|
}
|
|
providers.RegisterDomainServiceProviderType("HETZNER", fns, features)
|
|
}
|
|
|
|
// New creates a new API handle.
|
|
func New(settings map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) {
|
|
if settings["api_key"] == "" {
|
|
return nil, fmt.Errorf("missing HETZNER api_key")
|
|
}
|
|
|
|
api := &hetznerProvider{}
|
|
|
|
api.apiKey = settings["api_key"]
|
|
|
|
if settings["rate_limited"] == "true" {
|
|
// backwards compatibility
|
|
settings["start_with_default_rate_limit"] = "true"
|
|
}
|
|
if settings["start_with_default_rate_limit"] == "true" {
|
|
api.startRateLimited()
|
|
}
|
|
|
|
quota := settings["optimize_for_rate_limit_quota"]
|
|
err := api.requestRateLimiter.setOptimizeForRateLimitQuota(quota)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unexpected value for optimize_for_rate_limit_quota: %w", err)
|
|
}
|
|
|
|
return api, nil
|
|
}
|
|
|
|
// EnsureDomainExists creates the domain if it does not exist.
|
|
func (api *hetznerProvider) EnsureDomainExists(domain string) error {
|
|
domains, err := api.ListZones()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, d := range domains {
|
|
if d == domain {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// reset zone cache
|
|
api.zones = nil
|
|
return api.createZone(domain)
|
|
}
|
|
|
|
// GetDomainCorrections returns the corrections for a domain.
|
|
func (api *hetznerProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
dc, err := dc.Copy()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = dc.Punycode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
domain := dc.Name
|
|
|
|
// Get existing records
|
|
existingRecords, err := api.GetZoneRecords(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Normalize
|
|
models.PostProcessRecords(existingRecords)
|
|
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
|
|
|
|
differ := diff.New(dc)
|
|
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var corrections []*models.Correction
|
|
|
|
zone, err := api.getZone(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, m := range del {
|
|
record := m.Existing.Original.(*record)
|
|
corr := &models.Correction{
|
|
Msg: m.String(),
|
|
F: func() error {
|
|
return api.deleteRecord(*record)
|
|
},
|
|
}
|
|
corrections = append(corrections, corr)
|
|
}
|
|
|
|
var createRecords []record
|
|
createDescription := []string{"Batch creation of records:"}
|
|
for _, m := range create {
|
|
record := fromRecordConfig(m.Desired, zone)
|
|
createRecords = append(createRecords, *record)
|
|
createDescription = append(createDescription, m.String())
|
|
}
|
|
if len(createRecords) > 0 {
|
|
corr := &models.Correction{
|
|
Msg: strings.Join(createDescription, "\n\t"),
|
|
F: func() error {
|
|
return api.bulkCreateRecords(createRecords)
|
|
},
|
|
}
|
|
corrections = append(corrections, corr)
|
|
}
|
|
|
|
var modifyRecords []record
|
|
modifyDescription := []string{"Batch modification of records:"}
|
|
for _, m := range modify {
|
|
id := m.Existing.Original.(*record).ID
|
|
record := fromRecordConfig(m.Desired, zone)
|
|
record.ID = id
|
|
modifyRecords = append(modifyRecords, *record)
|
|
modifyDescription = append(modifyDescription, m.String())
|
|
}
|
|
if len(modifyRecords) > 0 {
|
|
corr := &models.Correction{
|
|
Msg: strings.Join(modifyDescription, "\n\t"),
|
|
F: func() error {
|
|
return api.bulkUpdateRecords(modifyRecords)
|
|
},
|
|
}
|
|
corrections = append(corrections, corr)
|
|
}
|
|
|
|
return corrections, nil
|
|
}
|
|
|
|
// GetNameservers returns the nameservers for a domain.
|
|
func (api *hetznerProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
|
zone, err := api.getZone(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nameserver := make([]*models.Nameserver, len(zone.NameServers))
|
|
for i := range zone.NameServers {
|
|
nameserver[i] = &models.Nameserver{Name: zone.NameServers[i]}
|
|
}
|
|
return nameserver, nil
|
|
}
|
|
|
|
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
|
func (api *hetznerProvider) GetZoneRecords(domain string) (models.Records, error) {
|
|
records, err := api.getAllRecords(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
existingRecords := make([]*models.RecordConfig, len(records))
|
|
for i := range records {
|
|
existingRecords[i] = toRecordConfig(domain, &records[i])
|
|
}
|
|
return existingRecords, nil
|
|
}
|
|
|
|
// ListZones lists the zones on this account.
|
|
func (api *hetznerProvider) ListZones() ([]string, error) {
|
|
if err := api.getAllZones(); err != nil {
|
|
return nil, err
|
|
}
|
|
var zones []string
|
|
for i := range api.zones {
|
|
zones = append(zones, i)
|
|
}
|
|
return zones, nil
|
|
}
|