github.com/teknogeek/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  }