github.com/kurthockenbury/dnscontrol@v0.2.8/providers/octodns/octoyaml/sort.go (about) 1 package octoyaml 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net" 8 "sort" 9 10 "github.com/StackExchange/dnscontrol/models" 11 "github.com/StackExchange/dnscontrol/pkg/natsort" 12 "github.com/miekg/dns/dnsutil" 13 ) 14 15 type genYamlData struct { 16 Origin string 17 DefaultTTL uint32 18 Records models.Records 19 } 20 21 func sortRecs(recs models.Records, origin string) { 22 z := &genYamlData{ 23 Origin: dnsutil.AddOrigin(origin, "."), 24 Records: recs, 25 } 26 sort.Sort(z) 27 } 28 29 func (z *genYamlData) Len() int { return len(z.Records) } 30 func (z *genYamlData) Swap(i, j int) { z.Records[i], z.Records[j] = z.Records[j], z.Records[i] } 31 func (z *genYamlData) Less(i, j int) bool { 32 a, b := z.Records[i], z.Records[j] 33 compA, compB := a.GetLabel(), b.GetLabel() 34 if compA != compB { 35 if compA == z.Origin+"." { 36 compA = "@" 37 } 38 if compB == z.Origin+"." { 39 compB = "@" 40 } 41 return zoneLabelLess(compA, compB) 42 } 43 rrtypeA, rrtypeB := a.Type, b.Type 44 if rrtypeA != rrtypeB { 45 return zoneRrtypeLess(rrtypeA, rrtypeB) 46 } 47 switch rrtypeA { // #rtype_variations 48 case "NS", "TXT", "TLSA": 49 // pass through. 50 case "A": 51 ta2, tb2 := net.ParseIP(a.GetTargetField()), net.ParseIP(b.GetTargetField()) 52 ipa, ipb := ta2.To4(), tb2.To4() 53 if ipa == nil || ipb == nil { 54 log.Fatalf("should not happen: IPs are not 4 bytes: %#v %#v", ta2, tb2) 55 } 56 return bytes.Compare(ipa, ipb) == -1 57 case "AAAA": 58 ta2, tb2 := net.ParseIP(a.GetTargetField()), net.ParseIP(b.GetTargetField()) 59 ipa, ipb := ta2.To16(), tb2.To16() 60 return bytes.Compare(ipa, ipb) == -1 61 case "MX": 62 pa, pb := a.MxPreference, b.MxPreference 63 return pa < pb 64 case "SRV": 65 pa, pb := a.SrvPort, b.SrvPort 66 if pa != pb { 67 return pa < pb 68 } 69 pa, pb = a.SrvPriority, b.SrvPriority 70 if pa != pb { 71 return pa < pb 72 } 73 pa, pb = a.SrvWeight, a.SrvWeight 74 if pa != pb { 75 return pa < pb 76 } 77 case "PTR": 78 pa, pb := a.GetTargetField(), b.GetTargetField() 79 if pa != pb { 80 return pa < pb 81 } 82 case "CAA": 83 // sort by tag 84 pa, pb := a.CaaTag, b.CaaTag 85 if pa != pb { 86 return pa < pb 87 } 88 // then flag 89 fa, fb := a.CaaFlag, b.CaaFlag 90 if fa != fb { 91 // flag set goes before ones without flag set 92 return fa > fb 93 } 94 default: 95 panic(fmt.Sprintf("genYamlData Less: unimplemented rtype %v", a.Type)) 96 // We panic so that we quickly find any switch statements 97 // that have not been updated for a new RR type. 98 } 99 return a.GetTargetSortable() < b.GetTargetSortable() 100 } 101 102 func zoneLabelLess(a, b string) bool { 103 return natsort.Less(a, b) 104 // octodns-validate wants a "natural sort" (i.e. foo10 comes after foo3). 105 // We emulate this with the natsort package. 106 // If you need to disable that validatation: 107 // Edit env/lib/python2.7/site-packages/octodns/yaml.py 108 // Change line 27: OLD: if key != expected 109 // NEW: if False and key != expected 110 } 111 112 func zoneRrtypeLess(a, b string) bool { 113 // Compare two RR types for the purpose of sorting the RRs in a Zone. 114 115 // If they are equal, we are done. All other code is simplified 116 // because we can assume a!=b. 117 if a == b { 118 return false 119 } 120 121 // List SOAs, then NSs, then all others. 122 // i.e. SOA is always less. NS is less than everything but SOA. 123 if a == "SOA" { 124 return true 125 } 126 if b == "SOA" { 127 return false 128 } 129 if a == "NS" { 130 return true 131 } 132 if b == "NS" { 133 return false 134 } 135 return a < b 136 }