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-01-28 23:42:31 +08:00
"github.com/StackExchange/dnscontrol/v2/models"
"github.com/StackExchange/dnscontrol/v2/providers"
"github.com/StackExchange/dnscontrol/v2/providers/diff"
"github.com/StackExchange/dnscontrol/v2/providers/octodns/octoyaml"
2018-02-28 06:36:47 +08:00
"github.com/pkg/errors"
)
var features = providers . DocumentationNotes {
//providers.CanUseCAA: providers.Can(),
providers . CanUsePTR : providers . Can ( ) ,
providers . CanUseSRV : providers . Can ( ) ,
//providers.CanUseTXTMulti: providers.Can(),
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." ) ,
}
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)
api := & Provider {
directory : config [ "directory" ] ,
}
if api . directory == "" {
api . directory = "config"
}
if len ( providermeta ) != 0 {
err := json . Unmarshal ( providermeta , api )
if err != nil {
return nil , err
}
}
//api.nameservers = models.StringsToNameservers(api.DefaultNS)
return api , nil
}
func init ( ) {
providers . RegisterDomainServiceProviderType ( "OCTODNS" , initProvider , features )
}
// Provider is the provider handle for the OctoDNS driver.
type Provider struct {
//DefaultNS []string `json:"default_ns"`
//DefaultSoa SoaInfo `json:"default_soa"`
//nameservers []*models.Nameserver
directory string
}
// GetNameservers returns the nameservers for a domain.
func ( c * Provider ) GetNameservers ( string ) ( [ ] * models . Nameserver , error ) {
return nil , nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func ( c * Provider ) GetDomainCorrections ( dc * models . DomainConfig ) ( [ ] * models . Correction , error ) {
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 {
return nil , errors . Wrapf ( err , "can't open %s:" , zoneFileName )
}
} else {
foundRecords , err = octoyaml . ReadYaml ( foundFH , dc . Name )
if err != nil {
return nil , errors . Wrapf ( err , "can not get corrections" )
}
}
// Normalize
models . PostProcessRecords ( foundRecords )
differ := diff . New ( dc )
_ , create , del , mod := differ . IncrementalDiff ( foundRecords )
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
}