2018-02-28 06:36:47 +08:00
package octodns
/ *
octodns -
Generate zonefiles suitiable for OctoDNS .
The zonefiles are read and written to the directory octoconfig
If the old octoconfig files are readable , we read them to determine
if an update is actually needed .
The YAML input and output code is extremely complicated because
the format does not fit well with a statically typed language .
The YAML format changes drastically if the label has single
or multiple rtypes associated with it , and if there is a single
or multiple rtype data .
* /
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
2020-04-15 04:47:30 +08:00
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
2021-03-08 02:19:22 +08:00
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
2020-04-15 04:47:30 +08:00
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/StackExchange/dnscontrol/v3/providers/octodns/octoyaml"
2018-02-28 06:36:47 +08:00
)
var features = providers . DocumentationNotes {
//providers.CanUseCAA: providers.Can(),
2021-05-05 02:15:31 +08:00
providers . CanUsePTR : providers . Can ( ) ,
providers . CanUseSRV : providers . Can ( ) ,
2018-02-28 06:36:47 +08:00
providers . DocCreateDomains : providers . Cannot ( "Driver just maintains list of OctoDNS config files. You must manually create the master config files that refer these." ) ,
providers . DocDualHost : providers . Cannot ( "Research is needed." ) ,
2020-02-18 21:59:18 +08:00
providers . CanGetZones : providers . Unimplemented ( ) ,
2018-02-28 06:36:47 +08:00
}
func initProvider ( config map [ string ] string , providermeta json . RawMessage ) ( providers . DNSServiceProvider , error ) {
// config -- the key/values from creds.json
// meta -- the json blob from NewReq('name', 'TYPE', meta)
2020-10-26 21:25:30 +08:00
api := & octodnsProvider {
2018-02-28 06:36:47 +08:00
directory : config [ "directory" ] ,
}
if api . directory == "" {
api . directory = "config"
}
2022-02-12 03:30:45 +08:00
// Commented out because at this time api has no exported fields.
// if len(providermeta) != 0 {
// err := json.Unmarshal(providermeta, api)
// if err != nil {
// return nil, err
// }
// }
2018-02-28 06:36:47 +08:00
return api , nil
}
func init ( ) {
2021-03-08 02:19:22 +08:00
fns := providers . DspFuncs {
2021-05-05 02:15:31 +08:00
Initializer : initProvider ,
2021-03-09 09:14:30 +08:00
RecordAuditor : AuditRecords ,
2021-03-08 02:19:22 +08:00
}
providers . RegisterDomainServiceProviderType ( "OCTODNS" , fns , features )
2018-02-28 06:36:47 +08:00
}
2020-10-26 21:25:30 +08:00
// octodnsProvider is the provider handle for the OctoDNS driver.
type octodnsProvider struct {
2018-02-28 06:36:47 +08:00
//DefaultNS []string `json:"default_ns"`
//DefaultSoa SoaInfo `json:"default_soa"`
//nameservers []*models.Nameserver
directory string
}
// GetNameservers returns the nameservers for a domain.
2020-10-26 21:25:30 +08:00
func ( c * octodnsProvider ) GetNameservers ( string ) ( [ ] * models . Nameserver , error ) {
2018-02-28 06:36:47 +08:00
return nil , nil
}
2020-02-18 21:59:18 +08:00
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
2020-10-26 21:25:30 +08:00
func ( c * octodnsProvider ) GetZoneRecords ( domain string ) ( models . Records , error ) {
2020-02-18 21:59:18 +08:00
return nil , fmt . Errorf ( "not implemented" )
// This enables the get-zones subcommand.
// Implement this by extracting the code from GetDomainCorrections into
// a single function. For most providers this should be relatively easy.
}
2018-02-28 06:36:47 +08:00
// GetDomainCorrections returns a list of corrections to update a domain.
2020-10-26 21:25:30 +08:00
func ( c * octodnsProvider ) GetDomainCorrections ( dc * models . DomainConfig ) ( [ ] * models . Correction , error ) {
2018-02-28 06:36:47 +08:00
dc . Punycode ( )
// Phase 1: Copy everything to []*models.RecordConfig:
// expectedRecords < dc.Records[i]
// foundRecords < zonefile
//
// Phase 2: Do any manipulations:
// add NS
// manipulate SOA
//
// Phase 3: Convert to []diff.Records and compare:
// expectedDiffRecords < expectedRecords
// foundDiffRecords < foundRecords
// diff.Inc...(foundDiffRecords, expectedDiffRecords )
// Read foundRecords:
var foundRecords models . Records
zoneFileFound := true
zoneFileName := filepath . Join ( c . directory , strings . Replace ( strings . ToLower ( dc . Name ) , "/" , "_" , - 1 ) + ".yaml" )
foundFH , err := os . Open ( zoneFileName )
if err != nil {
if os . IsNotExist ( err ) {
zoneFileFound = false
} else {
2020-02-29 02:10:33 +08:00
return nil , fmt . Errorf ( "can't open %s: %w" , zoneFileName , err )
2018-02-28 06:36:47 +08:00
}
} else {
foundRecords , err = octoyaml . ReadYaml ( foundFH , dc . Name )
if err != nil {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
return nil , fmt . Errorf ( "can not get corrections: %w" , err )
2018-02-28 06:36:47 +08:00
}
}
// Normalize
models . PostProcessRecords ( foundRecords )
2021-03-08 02:19:22 +08:00
txtutil . SplitSingleLongTxt ( dc . Records ) // Autosplit long TXT records
2018-02-28 06:36:47 +08:00
differ := diff . New ( dc )
2020-08-21 03:49:00 +08:00
_ , create , del , mod , err := differ . IncrementalDiff ( foundRecords )
if err != nil {
return nil , err
}
2018-02-28 06:36:47 +08:00
buf := & bytes . Buffer { }
// Print a list of changes. Generate an actual change that is the zone
changes := false
for _ , i := range create {
changes = true
fmt . Fprintln ( buf , i )
}
for _ , i := range del {
changes = true
fmt . Fprintln ( buf , i )
}
for _ , i := range mod {
changes = true
fmt . Fprintln ( buf , i )
}
msg := fmt . Sprintf ( "GENERATE_CONFIGFILE: %s" , dc . Name )
if zoneFileFound {
msg += "\n"
msg += buf . String ( )
} else {
msg += fmt . Sprintf ( " (%d records)\n" , len ( create ) )
}
corrections := [ ] * models . Correction { }
if changes {
corrections = append ( corrections ,
& models . Correction {
Msg : msg ,
F : func ( ) error {
fmt . Printf ( "CREATING CONFIGFILE: %v\n" , zoneFileName )
zf , err := os . Create ( zoneFileName )
if err != nil {
log . Fatalf ( "Could not create zonefile: %v" , err )
}
//err = WriteZoneFile(zf, dc.Records, dc.Name)
err = octoyaml . WriteYaml ( zf , dc . Records , dc . Name )
if err != nil {
log . Fatalf ( "WriteZoneFile error: %v\n" , err )
}
err = zf . Close ( )
if err != nil {
log . Fatalf ( "Closing: %v" , err )
}
return nil
} ,
} )
}
return corrections , nil
}