diff options
| author | Felix Hanley <felix@userspace.com.au> | 2017-11-22 04:12:15 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2017-11-22 04:17:48 +0000 |
| commit | f80bcc60d27fd5fb6a5b1c638984f84cb03f91ea (patch) | |
| tree | 02da35d7ab94e3b56676ab845f9adff9b8efb1ae | |
| parent | f18488df5291dafe59b53585715f025d5c3c29b2 (diff) | |
| download | dyndnsd-f80bcc60d27fd5fb6a5b1c638984f84cb03f91ea.tar.gz dyndnsd-f80bcc60d27fd5fb6a5b1c638984f84cb03f91ea.tar.bz2 | |
Add basic NSD format with tests
| -rw-r--r-- | nsd.go | 47 | ||||
| -rw-r--r-- | nsd_test.go | 88 |
2 files changed, 135 insertions, 0 deletions
@@ -0,0 +1,47 @@ +package dyndns + +import ( + "bufio" + "bytes" + "fmt" + "regexp" + "strconv" + "text/template" +) + +type nsdFormat struct { +} + +func (f nsdFormat) Write(hosts []Host) ([]byte, error) { + var out bytes.Buffer + t := template.Must(template.New("zone").Parse(nsdZone)) + err := t.Execute(&out, hosts) + return out.Bytes(), err +} + +func (f nsdFormat) Read(b []byte) (out []Host, err error) { + re := regexp.MustCompile(nsdRE) + + scanner := bufio.NewScanner(bytes.NewReader(b)) + for scanner.Scan() { + r := re.FindStringSubmatch(scanner.Text()) + if len(r) != 5 { + return out, ErrInvalidZone + } + ttl, err := strconv.ParseInt(r[2], 10, 0) + if err != nil { + fmt.Printf("failed to parse zone entry %q\n", scanner.Text()) + return out, ErrInvalidZone + } + out = append(out, Host{Name: r[1], TTL: int(ttl), Type: r[3], Address: r[4]}) + } + return out, err +} + +const ( + // Regexp to read zone entries + nsdRE = `^\s*(\S+)\s+(\d+)\s+(A|AAAA)\s+(\S+)\s*$` + + // The zone file template + nsdZone = "{{range .}}{{.Name}} {{.TTL}} {{.Type}} {{.Address}}\n{{end}}" +) diff --git a/nsd_test.go b/nsd_test.go new file mode 100644 index 0000000..63bd658 --- /dev/null +++ b/nsd_test.go @@ -0,0 +1,88 @@ +package dyndns + +import ( + "github.com/google/go-cmp/cmp" + "testing" +) + +func TestNSDWrite(t *testing.T) { + nsd := nsdFormat{} + + var tests = []struct { + in interface{} + out interface{} + }{ + { + in: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}}, + out: []byte("test.com\t60\tA\t10.1.1.1\n"), + }, + { + in: []Host{ + {Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}, + {Name: "test2.com", TTL: 61, Type: "AAAA", Address: "10.1.1.2"}, + }, + out: []byte("test.com\t60\tA\t10.1.1.1\ntest2.com\t61\tAAAA\t10.1.1.2\n"), + }, + { + in: []Host{{Name: "test.com", TTL: 6, Type: "A", Address: "10.1.1.1"}}, + out: []byte("test.com\t6\tA\t10.1.1.1\n"), + }, + } + + for _, tt := range tests { + r, err := nsd.Write(tt.in.([]Host)) + if err != nil { + t.Errorf("Write(%q) failed, got %s\n", tt.in, err) + } + if !cmp.Equal(r, tt.out) { + t.Errorf("Write(%q) => %q, expected %q\n", tt.in, r, tt.out) + } + } +} + +func TestNSDRead(t *testing.T) { + nsd := nsdFormat{} + + var tests = []struct { + in interface{} + out interface{} + }{ + { + in: []byte("test.com\t60\tA\t10.1.1.1\n"), + out: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}}, + }, + { + in: []byte("test.com \t 60\t \tA\t\t\t10.1.1.1\n"), + out: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}}, + }, + { + in: []byte("test.com\t60\tA\t10.1.1.1\ntest2.com\t61\tAAAA\t10.1.1.2\n"), + out: []Host{ + {Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}, + {Name: "test2.com", TTL: 61, Type: "AAAA", Address: "10.1.1.2"}, + }, + }, + { + in: []byte("test.com\t6\tA\t10.1.1.1\n"), + out: []Host{{Name: "test.com", TTL: 6, Type: "A", Address: "10.1.1.1"}}, + }, + { + in: []byte("test.com\tA\t10.1.1.1\n"), + }, + { + in: []byte("test.com\t6 5\tA\t10.1.1.1\n"), + }, + } + + for _, tt := range tests { + r, err := nsd.Read(tt.in.([]byte)) + if err == nil { + if !cmp.Equal(r, tt.out) { + t.Errorf("Read(%q) => %q %q, expected %q\n", tt.in, r, err, tt.out) + + } + } else if tt.out != nil { + t.Errorf("Write(%q) failed, got %s\n", tt.in, err) + } + } +} |
