Skip to content

Commit

Permalink
Refactor: Use Go Generics for RecordConfig processing (#3359)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlimoncelli authored Jan 15, 2025
1 parent b1b2a2a commit 79d02ee
Show file tree
Hide file tree
Showing 20 changed files with 339 additions and 441 deletions.
48 changes: 17 additions & 31 deletions integrationTest/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,49 +529,41 @@ func withMeta(record *models.RecordConfig, metadata map[string]string) *models.R
return record
}

func makeRec2(typ string) *models.RecordConfig {
r := &models.RecordConfig{
Type: typ,
TTL: 300,
}
return r
}

func a(name string, a string) *models.RecordConfig {
rc, err := models.NewFromRawA([]string{name, a}, nil, "**current-domain**", 300)
rdata, err := models.ParseA([]string{a}, "**current-domain**")
if err != nil {
panic(err)
}
return rc
return models.MustCreateRecord(name, rdata, nil, 300, "**current-domain**")
}

func mx(name string, preference uint16, mx string) *models.RecordConfig {
rc := makeRec2("MX")
spreference := strconv.Itoa(int(preference))
if err := models.FromRaw(rc, "**current-domain**", "MX", []string{name, spreference, mx}, nil); err != nil {
rdata, err := models.ParseMX([]string{spreference, mx}, "**current-domain**")
if err != nil {
panic(err)
}

//fmt.Printf("DEBUG: MX Fields %v\n", rc.Fields)

if rc.Name != strings.ToLower(rc.Name) {
panic("MX short must be lowercase")
}
if rc.NameFQDN != strings.ToLower(rc.NameFQDN) {
panic("MX must be lowercase 2")
}
return rc
return models.MustCreateRecord(name, rdata, nil, 300, "**current-domain**")
}

func srv(name string, priority, weight, port uint16, target string) *models.RecordConfig {
rc := makeRec2("SRV")
spriority := strconv.Itoa(int(priority))
sweight := strconv.Itoa(int(weight))
sport := strconv.Itoa(int(port))
if err := models.FromRaw(rc, "**current-domain**", "SRV", []string{name, spriority, sweight, sport, target}, nil); err != nil {
rdata, err := models.ParseSRV([]string{spriority, sweight, sport, target}, "**current-domain**")
if err != nil {
panic(err)
}
return rc
return models.MustCreateRecord(name, rdata, nil, 300, "**current-domain**")
}

func cfSingleRedirect(name string, code uint16, when, then string) *models.RecordConfig {
scode := strconv.Itoa(int(code))
rdata, err := models.ParseCFSINGLEREDIRECT([]string{name, scode, when, then}, "**current-domain**")
if err != nil {
panic(err)
}
return models.MustCreateRecord(name, rdata, nil, 300, "**current-domain**")
}

func aaaa(name, target string) *models.RecordConfig {
Expand Down Expand Up @@ -614,12 +606,6 @@ func cfSingleRedirectEnabled() bool {
return ((*enableCFRedirectMode) != "")
}

func cfSingleRedirect(name string, code uint16, when, then string) *models.RecordConfig {
r := makeRec("@", name, "CF_SINGLE_REDIRECT")
panicOnErr(models.MakeSingleRedirectFromRawRec(r, code, name, when, then))
return r
}

func cfWorkerRoute(pattern, target string) *models.RecordConfig {
t := fmt.Sprintf("%s,%s", pattern, target)
r := makeRec("@", t, "CF_WORKER_ROUTE")
Expand Down
59 changes: 2 additions & 57 deletions models/cloudflare_from.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,8 @@ import (
"fmt"
)

// MakeSingleRedirectFromRawRec updates a RecordConfig to be a
// SINGLEREDIRECT using the data from a RawRecord.
func MakeSingleRedirectFromRawRec(rc *RecordConfig, code uint16, name, when, then string) error {
//// MakePageRule updates a RecordConfig to be a PAGE_RULE using PAGE_RULE data.
//func MakePageRule(rc *models.RecordConfig, priority int, code uint16, when, then string) error {
// if rc == nil {
// return errors.New("RecordConfig cannot be nil")
// }
// if when == "" || then == "" {
// return errors.New("when and then parameters cannot be empty")
// }
//
// display := mkPageRuleBlob(priority, code, when, then)
//
// rc.Type = "PAGE_RULE"
// rc.TTL = 1
// rc.CloudflareRedirect = &models.CloudflareSingleRedirectConfig{
// Code: code,
// //
// PRWhen: when,
// PRThen: then,
// PRPriority: priority,
// PRDisplay: display,
// }
// return rc.SetTarget(display)
//}
//
//// mkPageRuleBlob creates the 1,301,when,then string used in displays.
//func mkPageRuleBlob(priority int, code uint16, when, then string) string {
// return fmt.Sprintf("%d,%03d,%s,%s", priority, code, when, then)
//}
//
//// makeSingleRedirectFromRawRec updates a RecordConfig to be a
//// SINGLEREDIRECT using the data from a RawRecord.
//func makeSingleRedirectFromRawRec(rc *models.RecordConfig, code uint16, name, when, then string) error {
target := targetFromRaw(name, code, when, then)

rc.Type = "CF_SINGLE_REDIRECT"
rc.TTL = 1
rc.Fields = &CFSINGLEREDIRECT{
Code: code,
//
PRWhen: "UNKNOWABLE",
PRThen: "UNKNOWABLE",
PRPriority: 0,
PRDisplay: "UNKNOWABLE",
//
SRName: name,
SRWhen: when,
SRThen: then,
SRDisplay: target,
}
return rc.SetTarget(rc.AsCFSINGLEREDIRECT().SRDisplay)
}

// targetFromRaw create the display text used for a normal Redirect.
func targetFromRaw(name string, code uint16, when, then string) string {
// cfSingleRedirecttargetFromRaw create the display text used for a normal Redirect.
func cfSingleRedirecttargetFromRaw(name string, code uint16, when, then string) string {
return fmt.Sprintf("%s code=(%03d) when=(%s) then=(%s)",
name,
code,
Expand Down
96 changes: 44 additions & 52 deletions models/cloudflare_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package models

import (
"fmt"
"maps"
"strconv"

"github.com/StackExchange/dnscontrol/v4/pkg/fieldtypes"
)

func init() {
RegisterType("CF_SINGLE_REDIRECT", RegisterOpts{PopulateFromRaw: PopulateFromRawCFSINGLEREDIRECT})
MustRegisterType("CF_SINGLE_REDIRECT", RegisterOpts{PopulateFromRaw: PopulateFromRawCFSINGLEREDIRECT})
}

//// CFSINGLEREDIRECT
Expand All @@ -35,6 +34,40 @@ type CFSINGLEREDIRECT struct {
SRDisplay string `dns:"skip" json:"sr_display,omitempty"` // How is this displayed to the user (SetTarget) for CF_SINGLE_REDIRECT
}

func ParseCFSINGLEREDIRECT(rawfields []string, origin string) (CFSINGLEREDIRECT, error) {
var srname, srwhen, srthen string
var code uint16
var err error

if srname, err = fieldtypes.ParseStringTrimmed(rawfields[0]); err != nil {
return CFSINGLEREDIRECT{}, err
}
if code, err = fieldtypes.ParseUint16(rawfields[1]); err != nil {
return CFSINGLEREDIRECT{}, err
}
if srwhen, err = fieldtypes.ParseStringTrimmed(rawfields[2]); err != nil {
return CFSINGLEREDIRECT{}, err
}
if srthen, err = fieldtypes.ParseStringTrimmed(rawfields[3]); err != nil {
return CFSINGLEREDIRECT{}, err
}

return CFSINGLEREDIRECT{
PRWhen: "UNKNOWABLE",
PRThen: "UNKNOWABLE",
PRPriority: 0,
PRDisplay: "UNKNOWABLE",

SRName: srname,
Code: code,
SRWhen: srwhen,
SRThen: srthen,
SRRRulesetID: "",
SRRRulesetRuleID: "",
SRDisplay: cfSingleRedirecttargetFromRaw(srname, code, srwhen, srthen),
}, nil
}

func NewFromRawCFSINGLEREDIRECT(rawfields []string, meta map[string]string, origin string, ttl uint32) (*RecordConfig, error) {
rc := &RecordConfig{TTL: ttl}
if err := PopulateFromRawCFSINGLEREDIRECT(rc, rawfields, meta, origin); err != nil {
Expand All @@ -45,66 +78,25 @@ func NewFromRawCFSINGLEREDIRECT(rawfields []string, meta map[string]string, orig

// PopulateFromRawCFSINGLEREDIRECT updates rc to be an CFSINGLEREDIRECT record with contents from rawfields, meta and origin.
func PopulateFromRawCFSINGLEREDIRECT(rc *RecordConfig, rawfields []string, meta map[string]string, origin string) error {
var err error
rc.Type = "CF_SINGLE_REDIRECT"
rc.TTL = 1

// Error checking

if len(rawfields) <= 3 {
return fmt.Errorf("rtype CFSINGLEREDIRECT wants %d field(s), found %d: %+v", 1, len(rawfields)-1, rawfields[1:])
}

// Convert each rawfield.

rc.SetLabel(rawfields[0], origin) // Label

var srname string
if srname, err = fieldtypes.ParseStringTrimmed(rawfields[0]); err != nil {
return err
}
var code uint16
if code, err = fieldtypes.ParseUint16(rawfields[1]); err != nil {
return err
}
var srwhen string
if srwhen, err = fieldtypes.ParseStringTrimmed(rawfields[2]); err != nil {
return err
}
var srthen string
if srthen, err = fieldtypes.ParseStringTrimmed(rawfields[3]); err != nil {
// First rawfield is the label.
if err := rc.SetLabel3(rawfields[0], rc.SubDomain, origin); err != nil {
return err
}

return rc.PopulateCFSINGLEREDIRECTFields(srname, code, srwhen, srthen, meta, origin)
}

// PopulateCFSINGLEREDIRECTFields updates rc to be an CFSINGLEREDIRECT record with contents from typed data, meta, and origin.
func (rc *RecordConfig) PopulateCFSINGLEREDIRECTFields(srname string, code uint16,
srwhen, srthen string, meta map[string]string, origin string) error {
// Create the struct if needed.
if rc.Fields == nil {
rc.Fields = &CFSINGLEREDIRECT{}
}

// Process each field:

n := rc.Fields.(*CFSINGLEREDIRECT)
n.SRName = string(srname)
n.Code = uint16(code)
n.SRWhen = string(srwhen)
n.SRThen = string(srthen)

// Update legacy fields.
MakeSingleRedirectFromRawRec(rc, code, srname, srwhen, srthen)

// Update the RecordConfig:
if rc.Metadata == nil {
rc.Metadata = map[string]string{}
// Parse the remaining fields.
rdata, err := ParseCFSINGLEREDIRECT(rawfields, origin)
if err != nil {
return err
}
maps.Copy(rc.Metadata, meta) // Add the metadata
rc.Comparable = fmt.Sprintf("%q %d %q %q", srname, code, srwhen, srthen)
rc.Display = rc.Comparable

return nil
return RecordUpdateFields(rc, rdata, meta)
}

// AsCFSINGLEREDIRECT returns rc.Fields as an CFSINGLEREDIRECT struct.
Expand Down
4 changes: 1 addition & 3 deletions models/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package models

import (
"testing"
//"github.com/StackExchange/dnscontrol/v4/pkg/printer"
//"github.com/StackExchange/dnscontrol/v4/pkg/zonerecs"
)

func TestRR(t *testing.T) {
Expand All @@ -15,7 +13,7 @@ func TestRR(t *testing.T) {
TTL: 0,
MxPreference: 0,
}
experiment.ImportFromLegacy("example.com")
experiment.MustImportFromLegacy("example.com")
expected := "foo.example.com.\t300\tIN\tA\t1.2.3.4"
found := experiment.ToRR().String()
if found != expected {
Expand Down
27 changes: 21 additions & 6 deletions models/rawrecords.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func FromRaw(rc *RecordConfig, origin string, typeName string, args []string, me

rt, ok := rtypeDB[typeName]
if !ok {
return fmt.Errorf("unknown rtype %q", typeName)
return fmt.Errorf("unknown (FromRaw) rtype %q", typeName)
}

return rt.PopulateFromRaw(rc, args, meta, origin)
Expand All @@ -46,7 +46,9 @@ func CheckAndFixImport(recs []*RecordConfig, origin string) bool {
if IsTypeUpgraded(rec.Type) && rec.Fields == nil {
found = true
log.Warnf("LEGACY PROVIDER needs fixing! Created invalid record: %s %s %v\n", rec.Type, rec.Name, rec)
rec.ImportFromLegacy(origin)
if err := rec.ImportFromLegacy(origin); err != nil {
log.Warnf("Error fixing record: %s %s %v: %v\n", rec.Type, rec.Name, rec, err)
}
}
}
return found
Expand All @@ -67,15 +69,28 @@ func (rc *RecordConfig) ImportFromLegacy(origin string) error {
if err != nil {
return err
}
return rc.PopulateFieldsA(ip, nil, origin)
return RecordUpdateFields(rc, A{A: ip}, nil)
case "MX":
return rc.PopulateFieldsMX(rc.MxPreference, rc.target, nil, origin)
return RecordUpdateFields(rc,
MX{Preference: rc.MxPreference, Mx: rc.target},
nil,
)
case "SRV":
return rc.PopulateFieldsSRV(rc.SrvPriority, rc.SrvWeight, rc.SrvPort, rc.target, nil, origin)
return RecordUpdateFields(rc,
SRV{Priority: rc.SrvPriority, Weight: rc.SrvWeight, Port: rc.SrvPort, Target: rc.target},
nil,
)
}
panic("Should not happen")
}

// MustImportFromLegacy is like ImportFromLegacy but panics on error. Use only in tests and init() functions.
func (rc *RecordConfig) MustImportFromLegacy(origin string) {
if err := rc.ImportFromLegacy(origin); err != nil {
panic(err)
}
}

// TransformRawRecords converts the RawRecordConfigs from dnsconfig.js into RecordConfig.
func TransformRawRecords(domains []*DomainConfig) error {

Expand Down Expand Up @@ -107,7 +122,7 @@ func TransformRawRecords(domains []*DomainConfig) error {

rt, ok := rtypeDB[rawRec.Type]
if !ok {
return fmt.Errorf("unknown rtype %q", rawRec.Type)
return fmt.Errorf("unknown (TRR) rtype %q", rawRec.Type)
}

err := rt.PopulateFromRaw(rec, rawRec.Args, rec.Metadata, dc.Name)
Expand Down
12 changes: 8 additions & 4 deletions models/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,14 @@ func (rc *RecordConfig) Copy() (*RecordConfig, error) {
switch rc.Type {
case "A":
newR.Fields = &A{}
newR.Fields = rc.Fields
//newR.Fields = rc.Fields.(*A)
newR.Fields.(*A).A = rc.Fields.(*A).A
case "MX":
newR.Fields = &MX{}
newR.Fields = rc.Fields
newR.Fields = rc.Fields.(*MX)
case "SRV":
newR.Fields = &SRV{}
newR.Fields = rc.Fields
newR.Fields = rc.Fields.(*SRV)
}
//fmt.Printf("DEBUG: COPYING rc=%v new=%v\n", rc.Fields, newR.Fields)
return newR, err
Expand Down Expand Up @@ -654,7 +655,10 @@ func Downcase(recs []*RecordConfig) {
// Target is case insensitive. Downcase it.
r.target = strings.ToLower(r.target)
// BUGFIX(tlim): isn't ALIAS in the wrong case statement?
r.ImportFromLegacy("") // Convert legacy fields to raw fields.
if err := r.ImportFromLegacy(""); err != nil {
// Convert legacy fields to raw fields.
panic(err) // Should not happen.
}
case "A", "CAA", "CF_SINGLE_REDIRECT", "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE", "DHCID", "IMPORT_TRANSFORM", "LOC", "SSHFP", "TXT":
// Do nothing. (IP address or case sensitive target)
case "SOA":
Expand Down
Loading

0 comments on commit 79d02ee

Please sign in to comment.