mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
1206 lines
42 KiB
Go
1206 lines
42 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package plan
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"sigs.k8s.io/external-dns/endpoint"
|
|
"sigs.k8s.io/external-dns/internal/testutils"
|
|
)
|
|
|
|
type PlanTestSuite struct {
|
|
suite.Suite
|
|
fooV1Cname *endpoint.Endpoint
|
|
fooV2Cname *endpoint.Endpoint
|
|
fooV2CnameUppercase *endpoint.Endpoint
|
|
fooV2TXT *endpoint.Endpoint
|
|
fooV2CnameNoLabel *endpoint.Endpoint
|
|
fooV3CnameSameResource *endpoint.Endpoint
|
|
fooA5 *endpoint.Endpoint
|
|
fooAAAA *endpoint.Endpoint
|
|
dsA *endpoint.Endpoint
|
|
dsAAAA *endpoint.Endpoint
|
|
bar127A *endpoint.Endpoint
|
|
bar127AWithTTL *endpoint.Endpoint
|
|
bar127AWithProviderSpecificTrue *endpoint.Endpoint
|
|
bar127AWithProviderSpecificFalse *endpoint.Endpoint
|
|
bar127AWithProviderSpecificUnset *endpoint.Endpoint
|
|
bar192A *endpoint.Endpoint
|
|
multiple1 *endpoint.Endpoint
|
|
multiple2 *endpoint.Endpoint
|
|
multiple3 *endpoint.Endpoint
|
|
domainFilterFiltered1 *endpoint.Endpoint
|
|
domainFilterFiltered2 *endpoint.Endpoint
|
|
domainFilterFiltered3 *endpoint.Endpoint
|
|
domainFilterExcluded *endpoint.Endpoint
|
|
}
|
|
|
|
func (suite *PlanTestSuite) SetupTest() {
|
|
suite.fooV1Cname = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"v1"},
|
|
RecordType: "CNAME",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-v1",
|
|
endpoint.OwnerLabelKey: "pwner",
|
|
},
|
|
}
|
|
// same resource as fooV1Cname, but target is different. It will never be picked because its target lexicographically bigger than "v1"
|
|
suite.fooV3CnameSameResource = &endpoint.Endpoint{ // TODO: remove this once endpoint can support multiple targets
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"v3"},
|
|
RecordType: "CNAME",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-v1",
|
|
endpoint.OwnerLabelKey: "pwner",
|
|
},
|
|
}
|
|
suite.fooV2Cname = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"v2"},
|
|
RecordType: "CNAME",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-v2",
|
|
},
|
|
}
|
|
suite.fooV2CnameUppercase = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"V2"},
|
|
RecordType: "CNAME",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-v2",
|
|
},
|
|
}
|
|
suite.fooV2TXT = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
RecordType: "TXT",
|
|
}
|
|
suite.fooV2CnameNoLabel = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"v2"},
|
|
RecordType: "CNAME",
|
|
}
|
|
suite.fooA5 = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"5.5.5.5"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-5",
|
|
},
|
|
}
|
|
suite.fooAAAA = &endpoint.Endpoint{
|
|
DNSName: "foo",
|
|
Targets: endpoint.Targets{"2001:DB8::1"},
|
|
RecordType: "AAAA",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/foo-AAAA",
|
|
},
|
|
}
|
|
suite.dsA = &endpoint.Endpoint{
|
|
DNSName: "ds",
|
|
Targets: endpoint.Targets{"1.1.1.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/ds",
|
|
},
|
|
}
|
|
suite.dsAAAA = &endpoint.Endpoint{
|
|
DNSName: "ds",
|
|
Targets: endpoint.Targets{"2001:DB8::1"},
|
|
RecordType: "AAAA",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/ds-AAAAA",
|
|
},
|
|
}
|
|
suite.bar127A = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"127.0.0.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-127",
|
|
},
|
|
}
|
|
suite.bar127AWithTTL = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"127.0.0.1"},
|
|
RecordType: "A",
|
|
RecordTTL: 300,
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-127",
|
|
},
|
|
}
|
|
suite.bar127AWithProviderSpecificTrue = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"127.0.0.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-127",
|
|
},
|
|
ProviderSpecific: endpoint.ProviderSpecific{
|
|
endpoint.ProviderSpecificProperty{
|
|
Name: "alias",
|
|
Value: "false",
|
|
},
|
|
endpoint.ProviderSpecificProperty{
|
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
|
|
Value: "true",
|
|
},
|
|
},
|
|
}
|
|
suite.bar127AWithProviderSpecificFalse = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"127.0.0.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-127",
|
|
},
|
|
ProviderSpecific: endpoint.ProviderSpecific{
|
|
endpoint.ProviderSpecificProperty{
|
|
Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied",
|
|
Value: "false",
|
|
},
|
|
endpoint.ProviderSpecificProperty{
|
|
Name: "alias",
|
|
Value: "false",
|
|
},
|
|
},
|
|
}
|
|
suite.bar127AWithProviderSpecificUnset = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"127.0.0.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-127",
|
|
},
|
|
ProviderSpecific: endpoint.ProviderSpecific{
|
|
endpoint.ProviderSpecificProperty{
|
|
Name: "alias",
|
|
Value: "false",
|
|
},
|
|
},
|
|
}
|
|
suite.bar192A = &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
Targets: endpoint.Targets{"192.168.0.1"},
|
|
RecordType: "A",
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: "ingress/default/bar-192",
|
|
},
|
|
}
|
|
suite.multiple1 = &endpoint.Endpoint{
|
|
DNSName: "multiple",
|
|
Targets: endpoint.Targets{"192.168.0.1"},
|
|
RecordType: "A",
|
|
SetIdentifier: "test-set-1",
|
|
}
|
|
suite.multiple2 = &endpoint.Endpoint{
|
|
DNSName: "multiple",
|
|
Targets: endpoint.Targets{"192.168.0.2"},
|
|
RecordType: "A",
|
|
SetIdentifier: "test-set-1",
|
|
}
|
|
suite.multiple3 = &endpoint.Endpoint{
|
|
DNSName: "multiple",
|
|
Targets: endpoint.Targets{"192.168.0.2"},
|
|
RecordType: "A",
|
|
SetIdentifier: "test-set-2",
|
|
}
|
|
suite.domainFilterFiltered1 = &endpoint.Endpoint{
|
|
DNSName: "foo.domain.tld",
|
|
Targets: endpoint.Targets{"1.2.3.4"},
|
|
RecordType: "A",
|
|
}
|
|
suite.domainFilterFiltered2 = &endpoint.Endpoint{
|
|
DNSName: "bar.domain.tld",
|
|
Targets: endpoint.Targets{"1.2.3.5"},
|
|
RecordType: "A",
|
|
}
|
|
suite.domainFilterFiltered3 = &endpoint.Endpoint{
|
|
DNSName: "baz.domain.tld",
|
|
Targets: endpoint.Targets{"1.2.3.6"},
|
|
RecordType: "A",
|
|
}
|
|
suite.domainFilterExcluded = &endpoint.Endpoint{
|
|
DNSName: "foo.ex.domain.tld",
|
|
Targets: endpoint.Targets{"1.1.1.1"},
|
|
RecordType: "A",
|
|
}
|
|
}
|
|
|
|
func TestPlan_ChangesJson_DecodeEncode(t *testing.T) {
|
|
ch := &Changes{
|
|
Create: []*endpoint.Endpoint{
|
|
{
|
|
DNSName: "foo",
|
|
},
|
|
},
|
|
Update: []*Update{
|
|
{
|
|
Old: &endpoint.Endpoint{
|
|
DNSName: "bar",
|
|
},
|
|
New: &endpoint.Endpoint{
|
|
DNSName: "baz",
|
|
},
|
|
},
|
|
},
|
|
Delete: []*endpoint.Endpoint{
|
|
{
|
|
DNSName: "qux",
|
|
},
|
|
},
|
|
}
|
|
jsonBytes, err := json.Marshal(ch)
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t,
|
|
`{"create":[{"dnsName":"foo"}],"updateOld":[{"dnsName":"bar"}],"updateNew":[{"dnsName":"baz"}],"delete":[{"dnsName":"qux"}]}`,
|
|
string(jsonBytes))
|
|
var changes Changes
|
|
err = json.NewDecoder(bytes.NewBuffer(jsonBytes)).Decode(&changes)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, ch, &changes)
|
|
}
|
|
|
|
func TestPlan_ChangesJson_DecodeMixedCase(t *testing.T) {
|
|
input := `{"Create":[{"dnsName":"foo"}],"UpdateOld":[{"dnsName":"bar"}],"updateNew":[{"dnsName":"baz"}],"Delete":[{"dnsName":"qux"}]}`
|
|
var changes Changes
|
|
err := json.NewDecoder(strings.NewReader(input)).Decode(&changes)
|
|
require.NoError(t, err)
|
|
assert.Len(t, changes.Create, 1)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncFirstRound() {
|
|
current := []*endpoint.Endpoint{}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar127A} // v1 is chosen because of resolver taking "min"
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRound() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooV1Cname, suite.bar127A}
|
|
expectedCreate := []*endpoint.Endpoint{suite.bar127A}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundMigration() {
|
|
current := []*endpoint.Endpoint{suite.fooV2CnameNoLabel}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooV1Cname, suite.bar127A}
|
|
expectedCreate := []*endpoint.Endpoint{suite.bar127A}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV2CnameNoLabel}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithTTLChange() {
|
|
current := []*endpoint.Endpoint{suite.bar127A}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithTTL}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.bar127A}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithTTL}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificChange() {
|
|
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificNoChange() {
|
|
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
suite.False(changes.HasChanges())
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestHasChanges() {
|
|
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
suite.True(changes.HasChanges())
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificRemoval() {
|
|
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificAddition() {
|
|
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset}
|
|
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSyncSecondRoundWithOwnerInherited() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
expectedUpdateNew := []*endpoint.Endpoint{{
|
|
DNSName: suite.fooV2Cname.DNSName,
|
|
Targets: suite.fooV2Cname.Targets,
|
|
RecordType: suite.fooV2Cname.RecordType,
|
|
RecordTTL: suite.fooV2Cname.RecordTTL,
|
|
Labels: map[string]string{
|
|
endpoint.ResourceLabelKey: suite.fooV2Cname.Labels[endpoint.ResourceLabelKey],
|
|
endpoint.OwnerLabelKey: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
},
|
|
}}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestIdempotency() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestRecordTypeChange() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooA5}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooA5}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestExistingCNameWithDualStackDesired() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestExistingDualStackWithCNameDesired() {
|
|
suite.fooA5.Labels[endpoint.OwnerLabelKey] = "nerf"
|
|
suite.fooAAAA.Labels[endpoint.OwnerLabelKey] = "nerf"
|
|
current := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooA5.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
// TestExistingOwnerNotMatchingDualStackDesired validates that if there is an existing
|
|
// record for a domain but there is no ownership claim over it and there are desired
|
|
// records no changes are planed. Only domains that have explicit ownership claims should
|
|
// be updated.
|
|
func (suite *PlanTestSuite) TestExistingOwnerNotMatchingDualStackDesired() {
|
|
suite.fooA5.Labels = nil
|
|
current := []*endpoint.Endpoint{suite.fooA5}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: "pwner",
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
// TestConflictingCurrentNonConflictingDesired is a bit of a corner case as it would indicate
|
|
// that the provider is not following valid DNS rules or there may be some
|
|
// caching issues. In this case since the desired records are not conflicting
|
|
// the updates will end up with the conflict resolved.
|
|
func (suite *PlanTestSuite) TestConflictingCurrentNonConflictingDesired() {
|
|
suite.fooA5.Labels[endpoint.OwnerLabelKey] = suite.fooV1Cname.Labels[endpoint.OwnerLabelKey]
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5}
|
|
desired := []*endpoint.Endpoint{suite.fooA5}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
// TestConflictingCurrentNoDesired is a bit of a corner case as it would indicate
|
|
// that the provider is not following valid DNS rules or there may be some
|
|
// caching issues. In this case there are no desired enpoint candidates so plan
|
|
// on deleting the records.
|
|
func (suite *PlanTestSuite) TestConflictingCurrentNoDesired() {
|
|
suite.fooA5.Labels[endpoint.OwnerLabelKey] = suite.fooV1Cname.Labels[endpoint.OwnerLabelKey]
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5}
|
|
desired := []*endpoint.Endpoint{}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
// TestCurrentWithConflictingDesired simulates where the desired records result in conflicting records types.
|
|
// This could be the result of multiple sources generating conflicting records types. In this case the conflict
|
|
// resolver should prefer the A and AAAA record candidate and delete the other records.
|
|
func (suite *PlanTestSuite) TestCurrentWithConflictingDesired() {
|
|
suite.fooV1Cname.Labels[endpoint.OwnerLabelKey] = "nerf"
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5, suite.fooAAAA}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey],
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
// TestNoCurrentWithConflictingDesired simulates where the desired records result in conflicting records types.
|
|
// This could be the result of multiple sources generating conflicting records types. In this case there the
|
|
// conflict resolver should prefer the A and AAAA record and drop the other candidate record types.
|
|
func (suite *PlanTestSuite) TestNoCurrentWithConflictingDesired() {
|
|
current := []*endpoint.Endpoint{}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5, suite.fooAAAA}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestIgnoreTXT() {
|
|
current := []*endpoint.Endpoint{suite.fooV2TXT}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestExcludeTXT() {
|
|
current := []*endpoint.Endpoint{suite.fooV2TXT}
|
|
desired := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeTXT},
|
|
ExcludeRecords: []string{endpoint.RecordTypeTXT},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestIgnoreTargetCase() {
|
|
current := []*endpoint.Endpoint{suite.fooV2Cname}
|
|
desired := []*endpoint.Endpoint{suite.fooV2CnameUppercase}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestRemoveEndpoint() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.bar192A}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() {
|
|
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A}
|
|
desired := []*endpoint.Endpoint{suite.fooV1Cname}
|
|
expectedCreate := []*endpoint.Endpoint{}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&UpsertOnlyPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() {
|
|
current := []*endpoint.Endpoint{suite.multiple1}
|
|
desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3}
|
|
expectedCreate := []*endpoint.Endpoint{suite.multiple3}
|
|
expectedUpdateOld := []*endpoint.Endpoint{suite.multiple1}
|
|
expectedUpdateNew := []*endpoint.Endpoint{suite.multiple2}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestSetIdentifierUpdateCreatesAndDeletes() {
|
|
current := []*endpoint.Endpoint{suite.multiple2}
|
|
desired := []*endpoint.Endpoint{suite.multiple3}
|
|
expectedCreate := []*endpoint.Endpoint{suite.multiple3}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.multiple2}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestDomainFiltersInitial() {
|
|
current := []*endpoint.Endpoint{suite.domainFilterExcluded}
|
|
desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3}
|
|
expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
domainFilter := endpoint.NewDomainFilterWithExclusions([]string{"domain.tld"}, []string{"ex.domain.tld"})
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
DomainFilter: endpoint.MatchAllDomainFilters{domainFilter},
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestDomainFiltersUpdate() {
|
|
current := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2}
|
|
desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3}
|
|
expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered3}
|
|
expectedUpdateOld := []*endpoint.Endpoint{}
|
|
expectedUpdateNew := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{}
|
|
|
|
domainFilter := endpoint.NewDomainFilterWithExclusions([]string{"domain.tld"}, []string{"ex.domain.tld"})
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
DomainFilter: endpoint.MatchAllDomainFilters{domainFilter},
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectedUpdateNew)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectedUpdateOld)
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestAAAARecords() {
|
|
current := []*endpoint.Endpoint{}
|
|
desired := []*endpoint.Endpoint{suite.fooAAAA}
|
|
expectedCreate := []*endpoint.Endpoint{suite.fooAAAA}
|
|
expectNoChanges := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.Delete, expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectNoChanges)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestDualStackRecords() {
|
|
current := []*endpoint.Endpoint{}
|
|
desired := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
|
expectedCreate := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
|
expectNoChanges := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Create, expectedCreate)
|
|
validateEntries(suite.T(), changes.Delete, expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectNoChanges)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestDualStackRecordsDelete() {
|
|
current := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
|
desired := []*endpoint.Endpoint{}
|
|
expectedDelete := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
|
expectNoChanges := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
validateEntries(suite.T(), changes.Create, expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectNoChanges)
|
|
}
|
|
|
|
func (suite *PlanTestSuite) TestDualStackToSingleStack() {
|
|
current := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
|
desired := []*endpoint.Endpoint{suite.dsA}
|
|
expectedDelete := []*endpoint.Endpoint{suite.dsAAAA}
|
|
expectNoChanges := []*endpoint.Endpoint{}
|
|
|
|
p := &Plan{
|
|
Policies: []Policy{&SyncPolicy{}},
|
|
Current: current,
|
|
Desired: desired,
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
|
}
|
|
|
|
changes := p.Calculate().Changes
|
|
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
|
validateEntries(suite.T(), changes.Create, expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateOld(), expectNoChanges)
|
|
validateEntries(suite.T(), changes.UpdateNew(), expectNoChanges)
|
|
}
|
|
|
|
func TestPlan(t *testing.T) {
|
|
suite.Run(t, new(PlanTestSuite))
|
|
}
|
|
|
|
// validateEntries validates that the list of entries matches expected.
|
|
func validateEntries(t *testing.T, entries, expected []*endpoint.Endpoint) {
|
|
if !testutils.SameEndpoints(entries, expected) {
|
|
t.Fatalf("expected %q to match %q", entries, expected)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeDNSName(tt *testing.T) {
|
|
records := []struct {
|
|
dnsName string
|
|
expect string
|
|
}{
|
|
{
|
|
"3AAAA.FOO.BAR.COM ",
|
|
"3aaaa.foo.bar.com.",
|
|
},
|
|
{
|
|
" example.foo.com.",
|
|
"example.foo.com.",
|
|
},
|
|
{
|
|
"example123.foo.com ",
|
|
"example123.foo.com.",
|
|
},
|
|
{
|
|
"foo",
|
|
"foo.",
|
|
},
|
|
{
|
|
"123foo.bar",
|
|
"123foo.bar.",
|
|
},
|
|
{
|
|
"foo.com",
|
|
"foo.com.",
|
|
},
|
|
{
|
|
"foo.com.",
|
|
"foo.com.",
|
|
},
|
|
{
|
|
"_foo.com.",
|
|
"_foo.com.",
|
|
},
|
|
{
|
|
"\u005Ffoo.com.",
|
|
"_foo.com.",
|
|
},
|
|
{
|
|
".foo.com.",
|
|
".foo.com.",
|
|
},
|
|
{
|
|
"foo123.COM",
|
|
"foo123.com.",
|
|
},
|
|
{
|
|
"my-exaMple3.FOO.BAR.COM",
|
|
"my-example3.foo.bar.com.",
|
|
},
|
|
{
|
|
" my-example1214.FOO-1235.BAR-foo.COM ",
|
|
"my-example1214.foo-1235.bar-foo.com.",
|
|
},
|
|
{
|
|
"my-example-my-example-1214.FOO-1235.BAR-foo.COM",
|
|
"my-example-my-example-1214.foo-1235.bar-foo.com.",
|
|
},
|
|
{
|
|
"點看.org.",
|
|
"xn--c1yn36f.org.",
|
|
},
|
|
{
|
|
"nordic-ø.xn--kitty-點看pd34d.com",
|
|
"xn--nordic--w1a.xn--xn--kitty-pd34d-hn01b3542b.com.",
|
|
},
|
|
{
|
|
"nordic-ø.kitty😸.com.",
|
|
"xn--nordic--w1a.xn--kitty-pd34d.com.",
|
|
},
|
|
{
|
|
" nordic-ø.kitty😸.COM",
|
|
"xn--nordic--w1a.xn--kitty-pd34d.com.",
|
|
},
|
|
{
|
|
"xn--nordic--w1a.kitty😸.com.",
|
|
"xn--nordic--w1a.xn--kitty-pd34d.com.",
|
|
},
|
|
{
|
|
"*.example.com.",
|
|
"*.example.com.",
|
|
},
|
|
{
|
|
"*.example.com",
|
|
"*.example.com.",
|
|
},
|
|
}
|
|
for _, r := range records {
|
|
tt.Run(r.dnsName, func(t *testing.T) {
|
|
gotName := normalizeDNSName(r.dnsName)
|
|
assert.Equal(t, r.expect, gotName)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestShouldUpdateProviderSpecific(tt *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
current *endpoint.Endpoint
|
|
desired *endpoint.Endpoint
|
|
shouldUpdate bool
|
|
}{
|
|
{
|
|
name: "skip AWS target health",
|
|
current: &endpoint.Endpoint{
|
|
DNSName: "foo.com",
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "aws/evaluate-target-health", Value: "true"},
|
|
},
|
|
},
|
|
desired: &endpoint.Endpoint{
|
|
DNSName: "bar.com",
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "aws/evaluate-target-health", Value: "true"},
|
|
},
|
|
},
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "custom property unchanged",
|
|
current: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "custom/property", Value: "true"},
|
|
},
|
|
},
|
|
desired: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "custom/property", Value: "true"},
|
|
},
|
|
},
|
|
shouldUpdate: false,
|
|
},
|
|
{
|
|
name: "custom property value changed",
|
|
current: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "custom/property", Value: "true"},
|
|
},
|
|
},
|
|
desired: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "custom/property", Value: "false"},
|
|
},
|
|
},
|
|
shouldUpdate: true,
|
|
},
|
|
{
|
|
name: "custom property key changed",
|
|
current: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "custom/property", Value: "true"},
|
|
},
|
|
},
|
|
desired: &endpoint.Endpoint{
|
|
ProviderSpecific: []endpoint.ProviderSpecificProperty{
|
|
{Name: "new/property", Value: "true"},
|
|
},
|
|
},
|
|
shouldUpdate: true,
|
|
},
|
|
} {
|
|
tt.Run(test.name, func(t *testing.T) {
|
|
plan := &Plan{
|
|
Current: []*endpoint.Endpoint{test.current},
|
|
Desired: []*endpoint.Endpoint{test.desired},
|
|
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
|
}
|
|
b := plan.shouldUpdateProviderSpecific(test.desired, test.current)
|
|
assert.Equal(t, test.shouldUpdate, b)
|
|
})
|
|
}
|
|
}
|