2016-08-23 08:31:50 +08:00
|
|
|
package diff
|
|
|
|
|
|
|
|
import (
|
2017-01-12 03:38:07 +08:00
|
|
|
"strconv"
|
2016-08-23 08:31:50 +08:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2017-01-12 03:38:07 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/models"
|
2016-08-23 08:31:50 +08:00
|
|
|
"github.com/miekg/dns/dnsutil"
|
|
|
|
)
|
|
|
|
|
2017-01-12 03:38:07 +08:00
|
|
|
func myRecord(s string) *models.RecordConfig {
|
|
|
|
parts := strings.Split(s, " ")
|
|
|
|
ttl, _ := strconv.ParseUint(parts[2], 10, 32)
|
|
|
|
return &models.RecordConfig{
|
2018-01-16 04:39:29 +08:00
|
|
|
Name: parts[0],
|
2017-01-12 03:38:07 +08:00
|
|
|
NameFQDN: dnsutil.AddOrigin(parts[0], "example.com"),
|
|
|
|
Type: parts[1],
|
|
|
|
TTL: uint32(ttl),
|
|
|
|
Target: parts[3],
|
|
|
|
Metadata: map[string]string{},
|
|
|
|
}
|
2016-08-23 08:31:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestAdditionsOnly(t *testing.T) {
|
2017-01-12 03:38:07 +08:00
|
|
|
desired := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
myRecord("@ A 1 1.2.3.4"),
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
existing := []*models.RecordConfig{}
|
2016-08-23 08:31:50 +08:00
|
|
|
checkLengths(t, existing, desired, 0, 1, 0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeletionsOnly(t *testing.T) {
|
2017-01-12 03:38:07 +08:00
|
|
|
existing := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
myRecord("@ A 1 1.2.3.4"),
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
desired := []*models.RecordConfig{}
|
2016-08-23 08:31:50 +08:00
|
|
|
checkLengths(t, existing, desired, 0, 0, 1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestModification(t *testing.T) {
|
2017-01-12 03:38:07 +08:00
|
|
|
existing := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
myRecord("www A 1 1.1.1.1"),
|
|
|
|
myRecord("@ A 1 1.2.3.4"),
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
desired := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
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) {
|
2017-01-12 03:38:07 +08:00
|
|
|
existing := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
myRecord("www A 1 1.1.1.1"),
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
desired := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOutOfOrderRecords(t *testing.T) {
|
2017-01-12 03:38:07 +08:00
|
|
|
existing := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
myRecord("www A 1 1.1.1.1"),
|
|
|
|
myRecord("www A 1 2.2.2.2"),
|
|
|
|
myRecord("www A 1 3.3.3.3"),
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
desired := []*models.RecordConfig{
|
2016-08-23 08:31:50 +08:00
|
|
|
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 mods[0].Desired != desired[3] || mods[0].Existing != existing[2] {
|
|
|
|
t.Fatalf("Expected to match %s and %s, but matched %s and %s", existing[2], desired[3], mods[0].Existing, mods[0].Desired)
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
}
|
2016-08-23 08:31:50 +08:00
|
|
|
|
2017-01-12 03:38:07 +08:00
|
|
|
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"),
|
|
|
|
}
|
2017-07-20 03:53:40 +08:00
|
|
|
existing[0].MxPreference = 10
|
|
|
|
desired[0].MxPreference = 20
|
2017-01-12 03:38:07 +08:00
|
|
|
checkLengths(t, existing, desired, 0, 0, 0, 1)
|
2016-08-23 08:31:50 +08:00
|
|
|
}
|
|
|
|
|
2017-01-12 03:38:07 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-01-12 03:38:07 +08:00
|
|
|
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) {
|
2018-01-16 04:39:29 +08:00
|
|
|
return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...)
|
2017-05-03 23:46:39 +08:00
|
|
|
}
|
|
|
|
|
2018-01-16 04:39:29 +08:00
|
|
|
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) {
|
2017-01-12 03:38:07 +08:00
|
|
|
dc := &models.DomainConfig{
|
2018-01-16 04:39:29 +08:00
|
|
|
Name: "example.com",
|
|
|
|
Records: desired,
|
|
|
|
KeepUnknown: keepUnknown,
|
|
|
|
IgnoredLabels: ignoredRecords,
|
2017-01-12 03:38:07 +08:00
|
|
|
}
|
|
|
|
d := New(dc, valFuncs...)
|
|
|
|
un, cre, del, mod = d.IncrementalDiff(existing)
|
2016-08-23 08:31:50 +08:00
|
|
|
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)
|
|
|
|
}
|
2017-01-12 03:38:07 +08:00
|
|
|
if t.Failed() {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2016-08-23 08:31:50 +08:00
|
|
|
return
|
|
|
|
}
|
2017-05-03 23:46:39 +08:00
|
|
|
|
|
|
|
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"),
|
|
|
|
}
|
2018-01-16 04:39:29 +08:00
|
|
|
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"})
|
2017-05-03 23:46:39 +08:00
|
|
|
}
|