mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
PowerDNS: Implement diff2 differ and some cleanup (#2194)
Signed-off-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
This commit is contained in:
committed by
GitHub
parent
93e2b815fd
commit
302a74b935
129
providers/powerdns/diff.go
Normal file
129
providers/powerdns/diff.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package powerdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"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"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (dsp *powerdnsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
|
||||||
|
// create record diff by group
|
||||||
|
keysToUpdate, err := (diff.New(dc)).ChangedGroups(existing)
|
||||||
|
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 := canonical(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...)
|
||||||
|
|
||||||
|
return corrections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dsp *powerdnsProvider) getDiff2DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
|
||||||
|
changes, err := diff2.ByRecordSet(existing, dc, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var corrections []*models.Correction
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
labelName := canonical(change.Key.NameFQDN)
|
||||||
|
labelType := change.Key.Type
|
||||||
|
|
||||||
|
switch change.Type {
|
||||||
|
case diff2.REPORT:
|
||||||
|
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
|
||||||
|
case diff2.CREATE, diff2.CHANGE:
|
||||||
|
labelTTL := int(change.New[0].TTL)
|
||||||
|
records := buildRecordList(change)
|
||||||
|
|
||||||
|
corrections = append(corrections, &models.Correction{
|
||||||
|
Msg: change.MsgsJoined,
|
||||||
|
F: func() error {
|
||||||
|
return dsp.client.Zones().AddRecordSetToZone(context.Background(), dsp.ServerName, dc.Name, zones.ResourceRecordSet{
|
||||||
|
Name: labelName,
|
||||||
|
Type: labelType,
|
||||||
|
TTL: labelTTL,
|
||||||
|
Records: records,
|
||||||
|
ChangeType: zones.ChangeTypeReplace,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case diff2.DELETE:
|
||||||
|
corrections = append(corrections, &models.Correction{
|
||||||
|
Msg: change.MsgsJoined,
|
||||||
|
F: func() error {
|
||||||
|
return dsp.client.Zones().RemoveRecordSetFromZone(context.Background(), dsp.ServerName, dc.Name, labelName, labelType)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled change.Type %s", change.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return corrections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildRecordList returns a list of records for the PowerDNS resource record set from a change
|
||||||
|
func buildRecordList(change diff2.Change) (records []zones.Record) {
|
||||||
|
for _, recordContent := range change.New {
|
||||||
|
records = append(records, zones.Record{
|
||||||
|
Content: recordContent.GetTargetCombined(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func canonical(fqdn string) string {
|
||||||
|
return fqdn + "."
|
||||||
|
}
|
@ -2,14 +2,11 @@ package powerdns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/StackExchange/dnscontrol/v3/models"
|
"github.com/StackExchange/dnscontrol/v3/models"
|
||||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
|
||||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
|
||||||
"github.com/mittwald/go-powerdns/apis/zones"
|
"github.com/mittwald/go-powerdns/apis/zones"
|
||||||
"github.com/mittwald/go-powerdns/pdnshttp"
|
"github.com/mittwald/go-powerdns/pdnshttp"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetNameservers returns the nameservers for a domain.
|
// GetNameservers returns the nameservers for a domain.
|
||||||
@ -49,9 +46,8 @@ func (dsp *powerdnsProvider) GetZoneRecords(domain string) (models.Records, erro
|
|||||||
|
|
||||||
// GetDomainCorrections returns a list of corrections to update a domain.
|
// GetDomainCorrections returns a list of corrections to update a domain.
|
||||||
func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||||
|
|
||||||
// get current zone records
|
// get current zone records
|
||||||
curRecords, err := dsp.GetZoneRecords(dc.Name)
|
existing, err := dsp.GetZoneRecords(dc.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -60,80 +56,30 @@ func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
|
|||||||
if err := dc.Punycode(); err != nil {
|
if err := dc.Punycode(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
models.PostProcessRecords(curRecords)
|
models.PostProcessRecords(existing)
|
||||||
|
|
||||||
// create record diff by group
|
var corrections []*models.Correction
|
||||||
var keysToUpdate map[models.RecordKey][]string
|
|
||||||
if !diff2.EnableDiff2 {
|
if !diff2.EnableDiff2 {
|
||||||
keysToUpdate, err = (diff.New(dc)).ChangedGroups(curRecords)
|
corrections, err = dsp.getDiff1DomainCorrections(dc, existing)
|
||||||
} else {
|
} else {
|
||||||
keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(curRecords)
|
corrections, err = dsp.getDiff2DomainCorrections(dc, existing)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// DNSSec corrections
|
||||||
dnssecCorrections, err := dsp.getDNSSECCorrections(dc)
|
dnssecCorrections, err := dsp.getDNSSECCorrections(dc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
corrections = append(corrections, dnssecCorrections...)
|
|
||||||
|
|
||||||
return corrections, nil
|
return append(corrections, dnssecCorrections...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureZoneExists creates a zone if it does not exist
|
// EnsureZoneExists creates a zone if it does not exist
|
||||||
func (dsp *powerdnsProvider) EnsureZoneExists(domain string) error {
|
func (dsp *powerdnsProvider) EnsureZoneExists(domain string) error {
|
||||||
if _, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, domain+"."); err != nil {
|
if _, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, canonical(domain)); err != nil {
|
||||||
if e, ok := err.(pdnshttp.ErrUnexpectedStatus); ok {
|
if e, ok := err.(pdnshttp.ErrUnexpectedStatus); ok {
|
||||||
if e.StatusCode != http.StatusNotFound {
|
if e.StatusCode != http.StatusNotFound {
|
||||||
return err
|
return err
|
||||||
@ -144,7 +90,7 @@ func (dsp *powerdnsProvider) EnsureZoneExists(domain string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := dsp.client.Zones().CreateZone(context.Background(), dsp.ServerName, zones.Zone{
|
_, err := dsp.client.Zones().CreateZone(context.Background(), dsp.ServerName, zones.Zone{
|
||||||
Name: domain + ".",
|
Name: canonical(domain),
|
||||||
Type: zones.ZoneTypeZone,
|
Type: zones.ZoneTypeZone,
|
||||||
DNSSec: dsp.DNSSecOnCreate,
|
DNSSec: dsp.DNSSecOnCreate,
|
||||||
Nameservers: dsp.DefaultNS,
|
Nameservers: dsp.DefaultNS,
|
||||||
|
@ -33,7 +33,9 @@ func (dsp *powerdnsProvider) getDNSSECCorrections(dc *models.DomainConfig) ([]*m
|
|||||||
return []*models.Correction{
|
return []*models.Correction{
|
||||||
{
|
{
|
||||||
Msg: "Disable DNSSEC",
|
Msg: "Disable DNSSEC",
|
||||||
F: func() error { _, err := dsp.removeDnssec(dc.Name, keyID); return err },
|
F: func() error {
|
||||||
|
return dsp.client.Cryptokeys().DeleteCryptokey(context.Background(), dsp.ServerName, dc.Name, keyID)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -43,33 +45,17 @@ func (dsp *powerdnsProvider) getDNSSECCorrections(dc *models.DomainConfig) ([]*m
|
|||||||
return []*models.Correction{
|
return []*models.Correction{
|
||||||
{
|
{
|
||||||
Msg: "Enable DNSSEC",
|
Msg: "Enable DNSSEC",
|
||||||
F: func() error { _, err := dsp.enableDnssec(dc.Name); return err },
|
F: func() (err error) {
|
||||||
|
_, err = dsp.client.Cryptokeys().CreateCryptokey(context.Background(), dsp.ServerName, dc.Name, cryptokeys.Cryptokey{
|
||||||
|
KeyType: "csk",
|
||||||
|
Active: true,
|
||||||
|
Published: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// enableDnssec creates a active and published cryptokey on this domain
|
|
||||||
func (dsp *powerdnsProvider) enableDnssec(domain string) (bool, error) {
|
|
||||||
// if there is now key, create one and enable it
|
|
||||||
_, err := dsp.client.Cryptokeys().CreateCryptokey(context.Background(), dsp.ServerName, domain, cryptokeys.Cryptokey{
|
|
||||||
KeyType: "csk",
|
|
||||||
Active: true,
|
|
||||||
Published: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeDnssec removes the cryptokey from this zone
|
|
||||||
func (dsp *powerdnsProvider) removeDnssec(domain string, keyID int) (bool, error) {
|
|
||||||
err := dsp.client.Cryptokeys().DeleteCryptokey(context.Background(), dsp.ServerName, domain, keyID)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
@ -15,7 +15,7 @@ var features = providers.DocumentationNotes{
|
|||||||
providers.CanUseAlias: providers.Can("Needs to be enabled in PowerDNS first", "https://doc.powerdns.com/authoritative/guides/alias.html"),
|
providers.CanUseAlias: providers.Can("Needs to be enabled in PowerDNS first", "https://doc.powerdns.com/authoritative/guides/alias.html"),
|
||||||
providers.CanUseCAA: providers.Can(),
|
providers.CanUseCAA: providers.Can(),
|
||||||
providers.CanUseDS: providers.Can(),
|
providers.CanUseDS: providers.Can(),
|
||||||
providers.CanUseLOC: providers.Unimplemented(),
|
providers.CanUseLOC: providers.Unimplemented("Normalization within the PowerDNS API seems to be buggy, so disabled", "https://github.com/PowerDNS/pdns/issues/10558"),
|
||||||
providers.CanUseNAPTR: providers.Can(),
|
providers.CanUseNAPTR: providers.Can(),
|
||||||
providers.CanUsePTR: providers.Can(),
|
providers.CanUsePTR: providers.Can(),
|
||||||
providers.CanUseSRV: providers.Can(),
|
providers.CanUseSRV: providers.Can(),
|
||||||
|
Reference in New Issue
Block a user