1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00
Files
stackexchange-dnscontrol/providers/diff/diff_test.go
Tom Limoncelli 9812ecd9ff BIND: Improve SOA serial number handling (#651)
* github.com/miekg/dns
* Greatly simplify the logic for handling serial numbers. Related code was all over the place. Now it is abstracted into one testable method makeSoa. This simplifies code in many other places.
* Update docs/_providers/bind.md: Edit old text. Add SOA description.
* SOA records are now treated like any other record internally. You still can't specify them in dnsconfig.js, but that's by design.
* The URL for issue 491 was wrong in many places
* BIND: Clarify GENERATE_ZONEFILE message
2020-02-23 13:58:49 -05:00

296 lines
8.7 KiB
Go

package diff
import (
"fmt"
"strconv"
"strings"
"testing"
"github.com/StackExchange/dnscontrol/v2/models"
)
func myRecord(s string) *models.RecordConfig {
parts := strings.Split(s, " ")
ttl, _ := strconv.ParseUint(parts[2], 10, 32)
r := &models.RecordConfig{
Type: parts[1],
TTL: uint32(ttl),
Metadata: map[string]string{},
}
r.SetLabel(parts[0], "example.com")
r.SetTarget(parts[3])
return r
}
func TestAdditionsOnly(t *testing.T) {
desired := []*models.RecordConfig{
myRecord("@ A 1 1.2.3.4"),
}
existing := []*models.RecordConfig{}
checkLengths(t, existing, desired, 0, 1, 0, 0)
}
func TestDeletionsOnly(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("@ A 1 1.2.3.4"),
}
desired := []*models.RecordConfig{}
checkLengths(t, existing, desired, 0, 0, 1, 0)
}
func TestModification(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("@ A 1 1.2.3.4"),
}
desired := []*models.RecordConfig{
myRecord("@ A 32 1.2.3.4"),
myRecord("www A 1 1.1.1.1"),
}
un, _, _, mod := checkLengths(t, existing, desired, 1, 0, 0, 1)
if un[0].Desired != desired[1] || un[0].Existing != existing[0] {
t.Error("Expected unchanged records to be correlated")
}
if mod[0].Desired != desired[0] || mod[0].Existing != existing[1] {
t.Errorf("Expected modified records to be correlated")
}
}
func TestUnchangedWithAddition(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www A 1 1.2.3.4"),
myRecord("www A 1 1.1.1.1"),
}
un, _, _, _ := checkLengths(t, existing, desired, 1, 1, 0, 0)
if un[0].Desired != desired[1] || un[0].Existing != existing[0] {
t.Errorf("Expected unchanged records to be correlated")
}
}
// s stringifies a RecordConfig for testing purposes.
func s(rc *models.RecordConfig) string {
return fmt.Sprintf("%s %s %d %s", rc.GetLabel(), rc.Type, rc.TTL, rc.GetTargetCombined())
}
func TestOutOfOrderRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("www A 1 2.2.2.2"),
myRecord("www A 1 3.3.3.3"),
}
desired := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("www A 1 2.2.2.2"),
myRecord("www A 1 2.2.2.3"),
myRecord("www A 10 3.3.3.3"),
}
_, _, _, mods := checkLengths(t, existing, desired, 2, 1, 0, 1)
if s(mods[0].Desired) != s(desired[3]) || s(mods[0].Existing) != s(existing[2]) {
t.Fatalf("Expected to match %s and %s, but matched %s and %s", s(existing[2]), s(desired[3]), s(mods[0].Existing), s(mods[0].Desired))
}
}
func TestMxPrio(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].MxPreference = 10
desired[0].MxPreference = 20
checkLengths(t, existing, desired, 0, 0, 0, 1)
}
func TestTTLChange(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 10 1.1.1.1"),
}
checkLengths(t, existing, desired, 0, 0, 0, 1)
}
func TestMetaChange(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].Metadata["k"] = "aa"
desired[0].Metadata["k"] = "bb"
checkLengths(t, existing, desired, 1, 0, 0, 0)
getMeta := func(r *models.RecordConfig) map[string]string {
return map[string]string{
"k": r.Metadata["k"],
}
}
checkLengths(t, existing, desired, 0, 0, 0, 1, getMeta)
}
func TestMetaOrdering(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].Metadata["k"] = "aa"
existing[0].Metadata["x"] = "cc"
desired[0].Metadata["k"] = "aa"
desired[0].Metadata["x"] = "cc"
checkLengths(t, existing, desired, 1, 0, 0, 0)
getMeta := func(r *models.RecordConfig) map[string]string {
return map[string]string{
"k": r.Metadata["k"],
}
}
checkLengths(t, existing, desired, 1, 0, 0, 0, getMeta)
}
func checkLengths(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...)
}
func checkLengthsWithKeepUnknown(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
return checkLengthsFull(t, existing, desired, unCount, createCount, delCount, modCount, keepUnknown, []string{}, valFuncs...)
}
func checkLengthsFull(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, ignoredRecords []string, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
dc := &models.DomainConfig{
Name: "example.com",
Records: desired,
KeepUnknown: keepUnknown,
IgnoredLabels: ignoredRecords,
}
d := New(dc, valFuncs...)
un, cre, del, mod = d.IncrementalDiff(existing)
if len(un) != unCount {
t.Errorf("Got %d unchanged records, but expected %d", len(un), unCount)
}
if len(cre) != createCount {
t.Errorf("Got %d records to create, but expected %d", len(cre), createCount)
}
if len(del) != delCount {
t.Errorf("Got %d records to delete, but expected %d", len(del), delCount)
}
if len(mod) != modCount {
t.Errorf("Got %d records to modify, but expected %d", len(mod), modCount)
}
if t.Failed() {
t.FailNow()
}
return
}
func TestNoPurge(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
myRecord("www MX 1 2.2.2.2"),
myRecord("www2 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
checkLengthsWithKeepUnknown(t, existing, desired, 1, 0, 1, 0, true)
}
func TestIgnoredRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www3 MX 1 2.2.2.2"),
}
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, []string{"www1", "www2"})
}
func TestModifyingIgnoredRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www2 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: modification of IGNOREd record")
}
}()
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, []string{"www1", "www2"})
}
func TestGlobIgnoredRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("foo.www2 MX 1 1.1.1.1"),
myRecord("foo.bar.www3 MX 1 1.1.1.1"),
myRecord("www4 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, []string{"www1", "*.www2", "**.www3"})
}
func TestInvalidGlobIgnoredRecord(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: invalid glob pattern for IGNORE")
}
}()
checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, []string{"www1", "www2", "[.www3"})
}
// from https://github.com/StackExchange/dnscontrol/issues/552
func TestCaas(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
}
existing[0].SetTargetCAA(3, "issue", "letsencrypt.org.")
existing[1].SetTargetCAA(3, "issue", "amazon.com.")
existing[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
// this will pass or fail depending on the ordering. Not ok.
desired[0].SetTargetCAA(3, "issue", "letsencrypt.org.")
desired[1].SetTargetCAA(3, "issue", "amazon.com.")
desired[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil)
// Make sure it passes with a different ordering. Not ok.
desired[2].SetTargetCAA(3, "issue", "letsencrypt.org.")
desired[1].SetTargetCAA(3, "issue", "amazon.com.")
desired[0].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil)
}