mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
NEW PROVIDER: AkamaiEdgeDNS (#1174)
* downcase TLSA * Akamai provider * Akamai provider * EdgeDNS provider * AkamaiEdgeDNS provider * AkamaiEdgeDNS provider * AkamaiEdgeDNS provider Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
@@ -4,6 +4,7 @@ package all
|
||||
import (
|
||||
// Define all known providers here. They should each register themselves with the providers package via init function.
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/activedir"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/akamaiedgedns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/axfrddns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/azuredns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/bind"
|
||||
|
247
providers/akamaiedgedns/akamaiEdgeDnsProvider.go
Normal file
247
providers/akamaiedgedns/akamaiEdgeDnsProvider.go
Normal file
@@ -0,0 +1,247 @@
|
||||
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"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var features = providers.DocumentationNotes{
|
||||
// The default for unlisted capabilities is 'Cannot'.
|
||||
// See providers/capabilities.go for the entire list of capabilties.
|
||||
providers.CanUseAlias: providers.Cannot(),
|
||||
providers.CanUseCAA: providers.Can(),
|
||||
providers.CanUseDS: providers.Cannot(),
|
||||
providers.CanUseDSForChildren: providers.Can(),
|
||||
providers.CanUsePTR: providers.Can(),
|
||||
providers.CanUseNAPTR: providers.Can(),
|
||||
providers.CanUseSRV: providers.Can(),
|
||||
providers.CanUseSSHFP: providers.Can(),
|
||||
providers.CanUseTLSA: providers.Can(),
|
||||
providers.CanAutoDNSSEC: providers.Can(),
|
||||
providers.CantUseNOPURGE: providers.Cannot(),
|
||||
providers.DocOfficiallySupported: providers.Cannot(),
|
||||
providers.DocDualHost: providers.Can(),
|
||||
providers.CanUseSOA: providers.Cannot(),
|
||||
providers.DocCreateDomains: providers.Can(),
|
||||
providers.CanGetZones: providers.Can(),
|
||||
providers.CanUseAKAMAICDN: providers.Can(),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
305
providers/akamaiedgedns/akamaiEdgeDnsService.go
Normal file
305
providers/akamaiedgedns/akamaiEdgeDnsService.go
Normal file
@@ -0,0 +1,305 @@
|
||||
package akamaiedgedns
|
||||
|
||||
/*
|
||||
For information about Akamai's "Edge DNS Zone Management API", see:
|
||||
https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html
|
||||
|
||||
For information about "AkamaiOPEN-edgegrid-golang" library, see:
|
||||
https://github.com/akamai/AkamaiOPEN-edgegrid-golang
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
|
||||
dnsv2 "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
)
|
||||
|
||||
// initialize initializes the "Akamai OPEN EdgeGrid" library
|
||||
func initialize(clientSecret string, host string, accessToken string, clientToken string) {
|
||||
|
||||
eg := edgegrid.Config{
|
||||
ClientSecret: clientSecret,
|
||||
Host: host,
|
||||
AccessToken: accessToken,
|
||||
ClientToken: clientToken,
|
||||
MaxBody: 131072,
|
||||
Debug: false,
|
||||
}
|
||||
dnsv2.Init(eg)
|
||||
}
|
||||
|
||||
// zoneDoesExist returns true if the zone exists, false otherwise.
|
||||
func zoneDoesExist(zonename string) bool {
|
||||
_, err := dnsv2.GetZone(zonename)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// createZone create a new zone and creates SOA and NS records for the zone.
|
||||
// Akamai assigns a unique set of authoritative nameservers for each contract. These authorities should be
|
||||
// used as the NS records on all zones belonging to this contract.
|
||||
func createZone(zonename string, contractID string, groupID string) error {
|
||||
zone := &dnsv2.ZoneCreate{
|
||||
Zone: zonename,
|
||||
Type: "PRIMARY",
|
||||
Comment: "This zone created by DNSControl (http://dnscontrol.org)",
|
||||
SignAndServe: false,
|
||||
SignAndServeAlgorithm: "RSA_SHA512",
|
||||
ContractId: contractID,
|
||||
}
|
||||
|
||||
queryArgs := &dnsv2.ZoneQueryString{
|
||||
Contract: contractID,
|
||||
Group: groupID,
|
||||
}
|
||||
|
||||
err := dnsv2.ValidateZone(zone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid value provided for zone. Error: %s", err.Error())
|
||||
}
|
||||
|
||||
err = zone.Save(*queryArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Zone create failed. Error: %s", err.Error())
|
||||
}
|
||||
|
||||
// Indirectly create NS and SOA records
|
||||
err = zone.SaveChangelist()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Zone initialization failed. SOA and NS records need to be created")
|
||||
}
|
||||
err = zone.SubmitChangelist()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Zone create failed. Error: %s", err.Error())
|
||||
}
|
||||
|
||||
printer.Printf("Created zone: %s\n", zone.Zone)
|
||||
printer.Printf(" Type: %s\n", zone.Type)
|
||||
printer.Printf(" Comment: %s\n", zone.Comment)
|
||||
printer.Printf(" SignAndServe: %v\n", zone.SignAndServe)
|
||||
printer.Printf(" SignAndServeAlgorithm: %s\n", zone.SignAndServeAlgorithm)
|
||||
printer.Printf(" ContractId: %s\n", zone.ContractId)
|
||||
printer.Printf(" GroupId: %s\n", queryArgs.Group)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listZones lists all zones associated with this contract.
|
||||
func listZones(contractID string) ([]string, error) {
|
||||
queryArgs := dnsv2.ZoneListQueryArgs{
|
||||
ContractIds: contractID,
|
||||
ShowAll: true,
|
||||
}
|
||||
|
||||
zoneListResp, err := dnsv2.ListZones(queryArgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Zone List retrieval failed. Error: %s", err.Error())
|
||||
}
|
||||
|
||||
edgeDNSZones := zoneListResp.Zones // what we have
|
||||
var zones []string // what we return
|
||||
|
||||
for _, edgeDNSZone := range edgeDNSZones {
|
||||
zones = append(zones, edgeDNSZone.Zone)
|
||||
}
|
||||
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
// isAutoDNSSecEnabled returns true if AutoDNSSEC (SignAndServe) is enabled, false otherwise.
|
||||
func isAutoDNSSecEnabled(zonename string) (bool, error) {
|
||||
zone, err := dnsv2.GetZone(zonename)
|
||||
if err != nil {
|
||||
if dnsv2.IsConfigDNSError(err) && err.(dnsv2.ConfigDNSError).NotFound() {
|
||||
return false, fmt.Errorf("Zone %s does not exist. Error: %s",
|
||||
zonename, err.Error())
|
||||
}
|
||||
return false, fmt.Errorf("Error retrieving information for zone %s. Error: %s",
|
||||
zonename, err.Error())
|
||||
}
|
||||
return zone.SignAndServe, nil
|
||||
}
|
||||
|
||||
// autoDNSSecEnable enables or disables AutoDNSSEC (SignAndServe) for the zone.
|
||||
func autoDNSSecEnable(enable bool, zonename string) error {
|
||||
zone, err := dnsv2.GetZone(zonename)
|
||||
if err != nil {
|
||||
if dnsv2.IsConfigDNSError(err) && err.(dnsv2.ConfigDNSError).NotFound() {
|
||||
return fmt.Errorf("Zone %s does not exist. Error: %s",
|
||||
zonename, err.Error())
|
||||
}
|
||||
return fmt.Errorf("Error retrieving information for zone %s. Error: %s",
|
||||
zonename, err.Error())
|
||||
}
|
||||
|
||||
algorithm := "RSA_SHA512"
|
||||
if zone.SignAndServeAlgorithm != "" {
|
||||
algorithm = zone.SignAndServeAlgorithm
|
||||
}
|
||||
|
||||
modifiedzone := &dnsv2.ZoneCreate{
|
||||
Zone: zone.Zone,
|
||||
Type: zone.Type,
|
||||
Masters: zone.Masters,
|
||||
Comment: zone.Comment,
|
||||
SignAndServe: enable, // AutoDNSSEC
|
||||
SignAndServeAlgorithm: algorithm,
|
||||
TsigKey: zone.TsigKey,
|
||||
EndCustomerId: zone.EndCustomerId,
|
||||
ContractId: zone.ContractId,
|
||||
}
|
||||
|
||||
queryArgs := dnsv2.ZoneQueryString{}
|
||||
|
||||
err = modifiedzone.Update(queryArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating zone %s. Error: %s",
|
||||
zonename, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAuthorities returns the list of authoritative nameservers for the contract.
|
||||
// Akamai assigns a unique set of authoritative nameservers for each contract. These authorities should be
|
||||
// used as the NS records on all zones belonging to this contract.
|
||||
func getAuthorities(contractID string) ([]string, error) {
|
||||
authorityResponse, err := dnsv2.GetAuthorities(contractID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getAuthorities - ContractID %s: Authorities retrieval failed. Error: %s",
|
||||
contractID, err.Error())
|
||||
}
|
||||
contracts := authorityResponse.Contracts
|
||||
if len(contracts) != 1 {
|
||||
return nil, fmt.Errorf("getAuthorities - ContractID %s: Expected 1 element in array but got %d",
|
||||
contractID, len(contracts))
|
||||
}
|
||||
cid := contracts[0].ContractID
|
||||
if cid != contractID {
|
||||
return nil, fmt.Errorf("getAuthorities - ContractID %s: Got authorities for wrong contractID (%s)",
|
||||
contractID, cid)
|
||||
}
|
||||
authorities := contracts[0].Authorities
|
||||
return authorities, nil
|
||||
}
|
||||
|
||||
// rcToRs converts DNSControl RecordConfig records to an AkamaiEdgeDNS recordset.
|
||||
func rcToRs(records []*models.RecordConfig, zonename string) (*dnsv2.RecordBody, error) {
|
||||
if len(records) == 0 {
|
||||
return nil, fmt.Errorf("No records to replace")
|
||||
}
|
||||
|
||||
akaRecord := &dnsv2.RecordBody{
|
||||
Name: records[0].NameFQDN,
|
||||
RecordType: records[0].Type,
|
||||
TTL: int(records[0].TTL),
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
akaRecord.Target = append(akaRecord.Target, r.GetTargetCombined())
|
||||
}
|
||||
|
||||
return akaRecord, nil
|
||||
}
|
||||
|
||||
// createRecordset creates a new AkamaiEdgeDNS recordset in the zone.
|
||||
func createRecordset(records []*models.RecordConfig, zonename string) error {
|
||||
akaRecord, err := rcToRs(records, zonename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = akaRecord.Save(zonename, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Recordset creation failed. Error: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceRecordset replaces an existing AkamaiEdgeDNS recordset in the zone.
|
||||
func replaceRecordset(records []*models.RecordConfig, zonename string) error {
|
||||
akaRecord, err := rcToRs(records, zonename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = akaRecord.Update(zonename, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Recordset update failed. Error: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteRecordset deletes an existing AkamaiEdgeDNS recordset in the zone.
|
||||
func deleteRecordset(records []*models.RecordConfig, zonename string) error {
|
||||
akaRecord, err := rcToRs(records, zonename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = akaRecord.Delete(zonename, true)
|
||||
if err != nil {
|
||||
if dnsv2.IsConfigDNSError(err) && err.(dnsv2.ConfigDNSError).NotFound() {
|
||||
return fmt.Errorf("Recordset not found")
|
||||
}
|
||||
return fmt.Errorf("Failed to delete recordset. Error: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Example AkamaiEdgeDNS Recordset (as JSON):
|
||||
{
|
||||
"name": "test.com",
|
||||
"rdata": [
|
||||
"a7.akafp.net.",
|
||||
"a4.akafp.net.",
|
||||
"a0.akafp.net."
|
||||
],
|
||||
"ttl": 10000,
|
||||
"type": "NS"
|
||||
}
|
||||
*/
|
||||
|
||||
// getRecords returns all RecordConfig records in the zone.
|
||||
func getRecords(zonename string) ([]*models.RecordConfig, error) {
|
||||
queryArgs := dnsv2.RecordsetQueryArgs{ShowAll: true}
|
||||
|
||||
rsetResp, err := dnsv2.GetRecordsets(zonename, queryArgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Recordset list retrieval failed. Error: %s", err.Error())
|
||||
}
|
||||
|
||||
akaRecordsets := rsetResp.Recordsets // what we have
|
||||
var recordConfigs []*models.RecordConfig // what we return
|
||||
|
||||
// For each AkamaiEdgeDNS recordset...
|
||||
for _, akarecset := range akaRecordsets {
|
||||
akaname := akarecset.Name
|
||||
akatype := akarecset.Type
|
||||
akattl := akarecset.TTL
|
||||
|
||||
// Don't report the existence of an SOA record (because DnsControl will try to delete the SOA record).
|
||||
if akatype == "SOA" {
|
||||
continue
|
||||
}
|
||||
|
||||
// ... convert the recordset into 1 or more RecordConfig structs
|
||||
for _, r := range akarecset.Rdata {
|
||||
rc := &models.RecordConfig{
|
||||
Type: akatype,
|
||||
TTL: uint32(akattl),
|
||||
}
|
||||
rc.SetLabelFromFQDN(akaname, zonename)
|
||||
err = rc.PopulateFromString(akatype, r, zonename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordConfigs = append(recordConfigs, rc)
|
||||
}
|
||||
}
|
||||
|
||||
return recordConfigs, nil
|
||||
}
|
8
providers/akamaiedgedns/auditrecords.go
Normal file
8
providers/akamaiedgedns/auditrecords.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package akamaiedgedns
|
||||
|
||||
import "github.com/StackExchange/dnscontrol/v3/models"
|
||||
|
||||
// AuditRecords returns an error if any records are not supportable by this provider.
|
||||
func AuditRecords(records []*models.RecordConfig) error {
|
||||
return nil
|
||||
}
|
@@ -69,6 +69,9 @@ const (
|
||||
|
||||
// CanUseSOA indicates the provider supports full management of a zone's SOA record
|
||||
CanUseSOA
|
||||
|
||||
// CanUseAKAMAICDN indicates the provider support the specific AKAMAICDN records that only the Akamai EdgeDns provider supports
|
||||
CanUseAKAMAICDN
|
||||
)
|
||||
|
||||
var providerCapabilities = map[string]map[Capability]bool{}
|
||||
|
Reference in New Issue
Block a user