dnscontrol/providers/akamaiedgedns/akamaiEdgeDnsProvider.go
Tom Limoncelli 444b893e1b
MAINT: Sort various lists (#1433)
* maint: sort lists in build/generate/featureMatrix.go

* maint: sort lists in docs/byo-secrets.md

* maint: sort lists in models/record.go

* maint: sort lists in pkg/normalize/validate.go

* maint: sort lists in providers/activedir/activedirProvider.go

* maint: sort lists in providers/akamaiedgedns/akamaiEdgeDnsProvider.go

* maint: sort lists in providers/axfrddns/axfrddnsProvider.go

* maint: sort lists in providers/azuredns/azureDnsProvider.go

* maint: sort lists in providers/cloudflare/cloudflareProvider.go

* maint: sort lists in providers/cloudns/cloudnsProvider.go

* maint: sort lists in providers/desec/desecProvider.go

* maint: sort lists in providers/digitalocean/digitaloceanProvider.go

* maint: sort lists in providers/dnsimple/dnsimpleProvider.go

* maint: sort lists in providers/dnsmadeeasy/dnsMadeEasyProvider.go

* maint: sort lists in providers/exoscale/exoscaleProvider.go

* maint: sort lists in providers/gandiv5/gandi_v5Provider.go

* maint: sort lists in providers/hedns/hednsProvider.go

* maint: sort lists in providers/hetzner/hetznerProvider.go

* maint: sort lists in providers/hexonet/hexonetProvider.go

* maint: sort lists in providers/inwx/inwxProvider.go

* maint: sort lists in providers/linode/linodeProvider.go

* maint: sort lists in providers/namecheap/namecheapProvider.go

* maint: sort lists in providers/namedotcom/namedotcomProvider.go

* maint: sort lists in providers/netcup/netcupProvider.go

* maint: sort lists in providers/ns1/ns1Provider.go

* maint: sort lists in providers/octodns/octodnsProvider.go

* maint: sort lists in providers/oracle/oracleProvider.go

* maint: sort lists in providers/ovh/ovhProvider.go

* maint: sort lists in providers/packetframe/packetframeProvider.go

* maint: sort lists in providers/powerdns/powerdnsProvider.go

* maint: sort lists in providers/route53/route53Provider.go

* maint: sort lists in providers/vultr/vultrProvider.go

* Update go:build pragmas for windows

* More sorting

* go generate
2022-03-02 11:19:15 -05:00

248 lines
7.9 KiB
Go

package akamaiedgedns
/*
Akamai Edge DNS provider
For information about Akamai Edge DNS, see:
https://www.akamai.com/us/en/products/security/edge-dns.jsp
https://learn.akamai.com/en-us/products/cloud_security/edge_dns.html
https://www.akamai.com/us/en/multimedia/documents/product-brief/edge-dns-product-brief.pdf
*/
import (
"encoding/json"
"fmt"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v3/providers"
)
var features = providers.DocumentationNotes{
// The default for unlisted capabilities is 'Cannot'.
// See providers/capabilities.go for the entire list of capabilties.
providers.CanAutoDNSSEC: providers.Can(),
providers.CanGetZones: providers.Can(),
providers.CanUseAKAMAICDN: providers.Can(),
providers.CanUseAlias: providers.Cannot(),
providers.CanUseCAA: providers.Can(),
providers.CanUseDS: providers.Cannot(),
providers.CanUseDSForChildren: providers.Can(),
providers.CanUseNAPTR: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSOA: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
providers.CanUseSSHFP: providers.Can(),
providers.CanUseTLSA: providers.Can(),
providers.CantUseNOPURGE: providers.Cannot(),
providers.DocCreateDomains: providers.Can(),
providers.DocDualHost: providers.Can(),
providers.DocOfficiallySupported: providers.Cannot(),
}
type edgeDNSProvider struct {
contractID string
groupID string
}
func init() {
fns := providers.DspFuncs{
Initializer: newEdgeDNSDSP,
RecordAuditor: AuditRecords,
}
providers.RegisterDomainServiceProviderType("AKAMAIEDGEDNS", fns, features)
providers.RegisterCustomRecordType("AKAMAICDN", "AKAMAIEDGEDNS", "")
}
// DnsServiceProvider
func newEdgeDNSDSP(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
clientSecret := config["client_secret"]
host := config["host"]
accessToken := config["access_token"]
clientToken := config["client_token"]
contractID := config["contract_id"]
groupID := config["group_id"]
if clientSecret == "" {
return nil, fmt.Errorf("creds.json: client_secret must not be empty")
}
if host == "" {
return nil, fmt.Errorf("creds.json: host must not be empty")
}
if accessToken == "" {
return nil, fmt.Errorf("creds.json: accessToken must not be empty")
}
if clientToken == "" {
return nil, fmt.Errorf("creds.json: clientToken must not be empty")
}
if contractID == "" {
return nil, fmt.Errorf("creds.json: contractID must not be empty")
}
if groupID == "" {
return nil, fmt.Errorf("creds.json: groupID must not be empty")
}
initialize(clientSecret, host, accessToken, clientToken)
api := &edgeDNSProvider{
contractID: contractID,
groupID: groupID,
}
return api, nil
}
// EnsureDomainExists configures a new zone if the zone does not already exist.
func (a *edgeDNSProvider) EnsureDomainExists(domain string) error {
if zoneDoesExist(domain) {
printer.Debugf("Zone %s already exists\n", domain)
return nil
}
return createZone(domain, a.contractID, a.groupID)
}
// GetDomainCorrections return a list of corrections. Each correction is a text string describing the change
// and a function that, if called, will make the change.
// “dnscontrol preview” simply prints the text strings.
// "dnscontrol push" prints the strings and calls the functions.
func (a *edgeDNSProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
existingRecords, err := getRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records)
keysToUpdate, err := (diff.New(dc)).ChangedGroups(existingRecords)
if err != nil {
return nil, err
}
existingRecordsMap := make(map[models.RecordKey][]*models.RecordConfig)
for _, r := range existingRecords {
key := models.RecordKey{NameFQDN: r.NameFQDN, Type: r.Type}
existingRecordsMap[key] = append(existingRecordsMap[key], r)
}
desiredRecordsMap := dc.Records.GroupedByKey()
// Deletes must occur first. For example, if replacing a existing CNAME with an A of the same name:
// DELETE CNAME foo.example.net
// must occur before
// CREATE A foo.example.net
// because both an A and a CNAME for the same name is not allowed.
corrections := []*models.Correction{} // deletes first
lastCorrections := []*models.Correction{} // creates and replaces last
for key, msg := range keysToUpdate {
existing, okExisting := existingRecordsMap[key]
desired, okDesired := desiredRecordsMap[key]
if okExisting && !okDesired {
// In the existing map but not in the desired map: Delete
corrections = append(corrections, &models.Correction{
Msg: strings.Join(msg, "\n "),
F: func() error {
return deleteRecordset(existing, dc.Name)
},
})
printer.Debugf("deleteRecordset: %s %s\n", key.NameFQDN, key.Type)
for _, rdata := range existing {
printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined())
}
} else if !okExisting && okDesired {
// Not in the existing map but in the desired map: Create
lastCorrections = append(lastCorrections, &models.Correction{
Msg: strings.Join(msg, "\n "),
F: func() error {
return createRecordset(desired, dc.Name)
},
})
printer.Debugf("createRecordset: %s %s\n", key.NameFQDN, key.Type)
for _, rdata := range desired {
printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined())
}
} else if okExisting && okDesired {
// In the existing map and in the desired map: Replace
lastCorrections = append(lastCorrections, &models.Correction{
Msg: strings.Join(msg, "\n "),
F: func() error {
return replaceRecordset(desired, dc.Name)
},
})
printer.Debugf("replaceRecordset: %s %s\n", key.NameFQDN, key.Type)
for _, rdata := range desired {
printer.Debugf(" Rdata: %s\n", rdata.GetTargetCombined())
}
}
}
// Deletes first, then creates and replaces
corrections = append(corrections, lastCorrections...)
// AutoDnsSec correction
existingAutoDNSSecEnabled, err := isAutoDNSSecEnabled(dc.Name)
if err != nil {
return nil, err
}
desiredAutoDNSSecEnabled := dc.AutoDNSSEC == "on"
if !existingAutoDNSSecEnabled && desiredAutoDNSSecEnabled {
// Existing false (disabled), Desired true (enabled)
corrections = append(corrections, &models.Correction{
Msg: "Enable AutoDnsSec\n",
F: func() error {
return autoDNSSecEnable(true, dc.Name)
},
})
printer.Debugf("autoDNSSecEnable: Enable AutoDnsSec for zone %s\n", dc.Name)
} else if existingAutoDNSSecEnabled && !desiredAutoDNSSecEnabled {
// Existing true (enabled), Desired false (disabled)
corrections = append(corrections, &models.Correction{
Msg: "Disable AutoDnsSec\n",
F: func() error {
return autoDNSSecEnable(false, dc.Name)
},
})
printer.Debugf("autoDNSSecEnable: Disable AutoDnsSec for zone %s\n", dc.Name)
}
return corrections, nil
}
// GetNameservers returns the nameservers for a domain.
func (a *edgeDNSProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
authorities, err := getAuthorities(a.contractID)
if err != nil {
return nil, err
}
return models.ToNameserversStripTD(authorities)
}
// GetZoneRecords returns an array of RecordConfig structs for a zone.
func (a *edgeDNSProvider) GetZoneRecords(domain string) (models.Records, error) {
records, err := getRecords(domain)
if err != nil {
return nil, err
}
return records, nil
}
// ListZones returns all DNS zones managed by this provider.
func (a *edgeDNSProvider) ListZones() ([]string, error) {
zones, err := listZones(a.contractID)
if err != nil {
return nil, err
}
return zones, nil
}