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"
"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(),
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." ) ,
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)
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
}
2020-02-18 21:59:18 +08:00
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
2020-06-18 21:37:57 +08:00
func ( c * Provider ) 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.
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 {
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 )
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
}