github.com/StackExchange/DNSControl@v0.2.8/providers/octodns/octoyaml/read.go (about) 1 package octoyaml 2 3 /* 4 This module handles reading OctoDNS yaml files. Sadly the YAML files 5 are so entirely flexible that parsing them is a nighmare. We UnMarshalYAML 6 them into a slice of interfaces mapped to interfaces, then use reflection 7 to walk the tree, interpreting what we find along the way. As we collect 8 data we output models.RecordConfig objects. 9 */ 10 11 import ( 12 "io" 13 "io/ioutil" 14 "math" 15 "reflect" 16 "strconv" 17 18 "github.com/StackExchange/dnscontrol/models" 19 "github.com/pkg/errors" 20 yaml "gopkg.in/yaml.v2" 21 ) 22 23 // ReadYaml parses a yaml input and returns a list of RecordConfigs 24 func ReadYaml(r io.Reader, origin string) (models.Records, error) { 25 results := models.Records{} 26 27 // Slurp the YAML into a string. 28 ydata, err := ioutil.ReadAll(r) 29 if err != nil { 30 return nil, errors.Wrapf(err, "can not read yaml filehandle") 31 } 32 33 // Unmarshal the mystery data into a structure we can relect into. 34 var mysterydata map[string]interface{} 35 err = yaml.Unmarshal(ydata, &mysterydata) 36 if err != nil { 37 return nil, errors.Wrapf(err, "could not unmarshal yaml") 38 } 39 //fmt.Printf("ReadYaml: mysterydata == %v\n", mysterydata) 40 41 // Traverse every key/value pair. 42 for k, v := range mysterydata { // Each label 43 // k, v: k is the label, v is everything we know about the label. 44 // In other code, k1, v2 refers to one level deeper, k3, k3 refers to 45 // one more level deeper, and so on. 46 //fmt.Printf("ReadYaml: NEXT KEY\n") 47 //fmt.Printf("ReadYaml: KEY=%s v.(type)=%s\n", k, reflect.TypeOf(v).String()) 48 switch v.(type) { 49 case map[interface{}]interface{}: 50 // The value is itself a map. This means we have a label with 51 // with one or more records, each of them are all the same rtype. 52 // parseLeaf will handle both of these forms: 53 // For example, this: 54 // 'www': 55 // type: A 56 // values: 57 // - 1.2.3.4 58 // - 1.2.3.5 59 // or 60 // 'www': 61 // type: CNAME 62 // value: foo.example.com. 63 results, err = parseLeaf(results, k, v, origin) 64 if err != nil { 65 return results, errors.Wrapf(err, "leaf (%v) error", v) 66 } 67 case []interface{}: 68 // The value is a list. This means we have a label with 69 // multiple records, each of them may be different rtypes. 70 // We need to call parseLeaf() once for each rtype. 71 // For example, this: 72 // 'www': 73 // - type: A 74 // values: 75 // - 1.2.3.4 76 // - 1.2.3.5 77 // - type: MX 78 // values: 79 // - priority: 10 80 // value: mx1.example.com. 81 // - priority: 10 82 // value: mx2.example.com. 83 for i, v3 := range v.([]interface{}) { // All the label's list 84 _ = i 85 //fmt.Printf("ReadYaml: list key=%s i=%d v3.(type)=%s\n", k, i, typeof(v3)) 86 switch v3.(type) { 87 case map[interface{}]interface{}: 88 //fmt.Printf("ReadYaml: v3=%v\n", v3) 89 results, err = parseLeaf(results, k, v3, origin) 90 if err != nil { 91 return results, errors.Wrapf(err, "leaf v3=%v", v3) 92 } 93 default: 94 return nil, errors.Errorf("unknown type in list3: k=%s v.(type)=%T v=%v", k, v, v) 95 } 96 } 97 98 default: 99 return nil, errors.Errorf("unknown type in list1: k=%s v.(type)=%T v=%v", k, v, v) 100 } 101 } 102 103 sortRecs(results, origin) 104 //fmt.Printf("ReadYaml: RESULTS=%v\n", results) 105 return results, nil 106 } 107 108 func parseLeaf(results models.Records, k string, v interface{}, origin string) (models.Records, error) { 109 var rType, rTarget string 110 var rTTL uint32 111 rTargets := []string{} 112 var someresults models.Records 113 for k2, v2 := range v.(map[interface{}]interface{}) { // All the label's items 114 // fmt.Printf("ReadYaml: ifs tk2=%s tv2=%s len(rTargets)=%d\n", typeof(k2), typeof(v2), len(rTargets)) 115 if typeof(k2) == "string" && (typeof(v2) == "string" || typeof(v2) == "int") { 116 // The 2nd level key is a string, and the 2nd level value is a string or int. 117 // Here are 3 examples: 118 // type: CNAME 119 // value: foo.example.com. 120 // ttl: 3 121 //fmt.Printf("parseLeaf: k2=%s v2=%v\n", k2, v2) 122 switch k2.(string) { 123 case "type": 124 rType = v2.(string) 125 case "ttl": 126 var err error 127 rTTL, err = decodeTTL(v2) 128 if err != nil { 129 return nil, errors.Errorf("parseLeaf: can not parse ttl (%v)", v2) 130 } 131 case "value": 132 rTarget = v2.(string) 133 case "values": 134 switch v2.(type) { 135 case string: 136 rTarget = v2.(string) 137 default: 138 return nil, errors.Errorf("parseLeaf: unknown type in values: rtpe=%s k=%s k2=%s v2.(type)=%T v2=%v", rType, k, k2, v2, v2) 139 } 140 default: 141 panic("Should not happen") 142 } 143 } else if typeof(k2) == "string" && typeof(v2) == "[]interface {}" { 144 // The 2nd level key is a string, and the 2nd level value is a list. 145 someresults = nil 146 for _, v3 := range v2.([]interface{}) { 147 switch v3.(type) { 148 case string: 149 // Example: 150 // values: 151 // - 1.2.3.1 152 // - 1.2.3.2 153 // - 1.2.3.3 154 // We collect all the values for later, when we'll need to generate 155 // one RecordConfig for each value. 156 //fmt.Printf("parseLeaf: s-append %s\n", v3.(string)) 157 rTargets = append(rTargets, v3.(string)) 158 case map[interface{}]interface{}: 159 // Example: 160 // values: 161 // - priority: 10 162 // value: mx1.example.com. 163 // - priority: 10 164 // value: mx2.example.com. 165 // We collect the individual values. When we are done with this level, 166 // we should have enough to generate a single RecordConfig. 167 newRc := newRecordConfig(k, rType, "", rTTL, origin) 168 for k4, v4 := range v3.(map[interface{}]interface{}) { 169 //fmt.Printf("parseLeaf: k4=%s v4=%s\n", k4, v4) 170 switch k4.(string) { 171 case "priority": // MX,SRV 172 priority := uint16(v4.(int)) 173 newRc.MxPreference = priority 174 newRc.SrvPriority = priority 175 // Assign it to both places. We'll zap the wrong one later. 176 case "weight": // SRV 177 newRc.SrvWeight = uint16(v4.(int)) 178 case "port": // SRV 179 newRc.SrvPort = uint16(v4.(int)) 180 case "value": // MX 181 newRc.SetTarget(v4.(string)) 182 } 183 } 184 //fmt.Printf("parseLeaf: append %v\n", newRc) 185 someresults = append(someresults, newRc) 186 default: 187 return nil, errors.Errorf("parseLeaf: unknown type in map: rtype=%s k=%s v3.(type)=%T v3=%v", rType, k, v3, v3) 188 } 189 } 190 } else { 191 return nil, errors.Errorf("parseLeaf: unknown type in level 2: k=%s k2=%s v.2(type)=%T v2=%v", k, k2, v2, v2) 192 } 193 } 194 // fmt.Printf("parseLeaf: Target=(%v)\n", rTarget) 195 // fmt.Printf("parseLeaf: len(rTargets)=%d\n", len(rTargets)) 196 // fmt.Printf("parseLeaf: len(someresults)=%d\n", len(someresults)) 197 198 // We've now looped through everything about one label. Make the RecordConfig(s). 199 200 if len(someresults) > 0 { 201 // We have many results. Generate a RecordConfig for each one. 202 for _, r := range someresults { 203 r.Type = rType 204 r.TTL = rTTL 205 results = append(results, r) 206 // Earlier we didn't know what the priority was for. Now that we know the rType, 207 // we zap the wrong one. 208 switch r.Type { 209 case "MX": 210 r.SrvPriority = 0 211 case "SRV": 212 r.MxPreference = 0 213 default: 214 panic("ugh") 215 } 216 } 217 } else if rTarget != "" && len(rTargets) == 0 { 218 // The file used "value". Generate a single RecordConfig 219 //fmt.Printf("parseLeaf: 1-newRecordConfig(%v, %v, %v, %v, %v)\n", k, rType, rTarget, rTTL, origin) 220 results = append(results, newRecordConfig(k, rType, rTarget, rTTL, origin)) 221 } else { 222 // The file used "values" so now we must generate a RecordConfig for each value. 223 for _, target := range rTargets { 224 //fmt.Printf("parseLeaf: 3-newRecordConfig(%v, %v, %v, %v, %v)\n", k, rType, target, rTTL, origin) 225 results = append(results, newRecordConfig(k, rType, target, rTTL, origin)) 226 } 227 } 228 return results, nil 229 } 230 231 // newRecordConfig is a RecordConfig factory. 232 func newRecordConfig(rname, rtype, target string, ttl uint32, origin string) *models.RecordConfig { 233 rc := &models.RecordConfig{ 234 Type: rtype, 235 TTL: ttl, 236 } 237 rc.SetLabel(rname, origin) 238 switch rtype { 239 case "TXT": 240 rc.SetTargetTXT(target) 241 default: 242 rc.SetTarget(target) 243 } 244 return rc 245 } 246 247 // typeof returns a string that indicates v's type: 248 func typeof(v interface{}) string { 249 // Cite: https://stackoverflow.com/a/20170555/71978 250 return reflect.TypeOf(v).String() 251 } 252 253 // decodeTTL decodes an interface into a TTL value. 254 // This is useful when you don't know if a TTL arrived as a string or int. 255 func decodeTTL(ttl interface{}) (uint32, error) { 256 switch ttl.(type) { 257 case uint32: 258 return ttl.(uint32), nil 259 case string: 260 s := ttl.(string) 261 t, err := strconv.ParseUint(s, 10, 32) 262 return uint32(t), errors.Wrapf(err, "decodeTTL failed to parse (%s)", s) 263 case int: 264 i := ttl.(int) 265 if i < 0 || i > math.MaxUint32 { 266 return 0, errors.Errorf("ttl won't fit in 32-bits (%d)", i) 267 } 268 return uint32(i), nil 269 } 270 panic("I don't know what type this TTL is") 271 }