dnscontrol/providers/powerdns/dns.go
Yannik Sembritzki 5e06c249d6
Rename DomainCreator to the more accurate ZoneCreator (#2038)
Co-authored-by: Yannik Sembritzki <yannik@sembritzki.org>
Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
2023-02-07 07:22:49 -05:00

154 lines
4.4 KiB
Go

package powerdns
import (
"context"
"net/http"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
"github.com/mittwald/go-powerdns/apis/zones"
"github.com/mittwald/go-powerdns/pdnshttp"
)
// GetNameservers returns the nameservers for a domain.
func (dsp *powerdnsProvider) GetNameservers(string) ([]*models.Nameserver, error) {
var r []string
for _, j := range dsp.nameservers {
r = append(r, j.Name)
}
return models.ToNameservers(r)
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (dsp *powerdnsProvider) GetZoneRecords(domain string) (models.Records, error) {
zone, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, domain)
if err != nil {
return nil, err
}
curRecords := models.Records{}
// loop over grouped records by type, called RRSet
for _, rrset := range zone.ResourceRecordSets {
if rrset.Type == "SOA" {
continue
}
// loop over single records of this group and create records
for _, pdnsRecord := range rrset.Records {
r, err := toRecordConfig(domain, pdnsRecord, rrset.TTL, rrset.Name, rrset.Type)
if err != nil {
return nil, err
}
curRecords = append(curRecords, r)
}
}
return curRecords, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// get current zone records
curRecords, err := dsp.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// post-process records
if err := dc.Punycode(); err != nil {
return nil, err
}
models.PostProcessRecords(curRecords)
// create record diff by group
var keysToUpdate map[models.RecordKey][]string
if !diff2.EnableDiff2 {
keysToUpdate, err = (diff.New(dc)).ChangedGroups(curRecords)
} else {
keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(curRecords)
}
if err != nil {
return nil, err
}
desiredRecords := dc.Records.GroupedByKey()
var cuCorrections []*models.Correction
var dCorrections []*models.Correction
// add create/update and delete corrections separately
for label, msgs := range keysToUpdate {
labelName := label.NameFQDN + "."
labelType := label.Type
msgJoined := strings.Join(msgs, "\n ")
if _, ok := desiredRecords[label]; !ok {
// no record found so delete it
dCorrections = append(dCorrections, &models.Correction{
Msg: msgJoined,
F: func() error {
return dsp.client.Zones().RemoveRecordSetFromZone(context.Background(), dsp.ServerName, dc.Name, labelName, labelType)
},
})
} else {
// record found so create or update it
ttl := desiredRecords[label][0].TTL
var records []zones.Record
for _, recordContent := range desiredRecords[label] {
records = append(records, zones.Record{
Content: recordContent.GetTargetCombined(),
})
}
cuCorrections = append(cuCorrections, &models.Correction{
Msg: msgJoined,
F: func() error {
return dsp.client.Zones().AddRecordSetToZone(context.Background(), dsp.ServerName, dc.Name, zones.ResourceRecordSet{
Name: labelName,
Type: labelType,
TTL: int(ttl),
Records: records,
ChangeType: zones.ChangeTypeReplace,
})
},
})
}
}
// append corrections in the right order
// delete corrections must be run first to avoid correlations with existing RR
var corrections []*models.Correction
corrections = append(corrections, dCorrections...)
corrections = append(corrections, cuCorrections...)
// DNSSec corrections
dnssecCorrections, err := dsp.getDNSSECCorrections(dc)
if err != nil {
return nil, err
}
corrections = append(corrections, dnssecCorrections...)
return corrections, nil
}
// EnsureZoneExists creates a zone if it does not exist
func (dsp *powerdnsProvider) EnsureZoneExists(domain string) error {
if _, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, domain+"."); err != nil {
if e, ok := err.(pdnshttp.ErrUnexpectedStatus); ok {
if e.StatusCode != http.StatusNotFound {
return err
}
}
} else { // zone seems to exist
return nil
}
_, err := dsp.client.Zones().CreateZone(context.Background(), dsp.ServerName, zones.Zone{
Name: domain + ".",
Type: zones.ZoneTypeZone,
DNSSec: dsp.DNSSecOnCreate,
Nameservers: dsp.DefaultNS,
})
return err
}