mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Separate plan by record type
This commit is contained in:
parent
06227c1fbf
commit
94d09b37dc
@ -30,6 +30,8 @@ import (
|
||||
const (
|
||||
// RecordTypeA is a RecordType enum value
|
||||
RecordTypeA = "A"
|
||||
// RecordTypeAAAA is a RecordType enum value
|
||||
RecordTypeAAAA = "AAAA"
|
||||
// RecordTypeCNAME is a RecordType enum value
|
||||
RecordTypeCNAME = "CNAME"
|
||||
// RecordTypeTXT is a RecordType enum value
|
||||
|
60
plan/plan.go
60
plan/plan.go
@ -78,12 +78,12 @@ bar.com | | [->191.1.1.1, ->190.1.1.1] | = create (bar.com -> 1
|
||||
"=", i.e. result of calculation relies on supplied ConflictResolver
|
||||
*/
|
||||
type planTable struct {
|
||||
rows map[string]map[string]*planTableRow
|
||||
rows map[string]map[string]map[string]*planTableRow
|
||||
resolver ConflictResolver
|
||||
}
|
||||
|
||||
func newPlanTable() planTable { // TODO: make resolver configurable
|
||||
return planTable{map[string]map[string]*planTableRow{}, PerResource{}}
|
||||
return planTable{map[string]map[string]map[string]*planTableRow{}, PerResource{}}
|
||||
}
|
||||
|
||||
// planTableRow
|
||||
@ -101,23 +101,29 @@ func (t planTableRow) String() string {
|
||||
func (t planTable) addCurrent(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
t.rows[dnsName] = make(map[string]map[string]*planTableRow)
|
||||
}
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow)
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier].current = e
|
||||
if _, ok := t.rows[e.SetIdentifier][e.RecordType]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{}
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier][e.RecordType].current = e
|
||||
}
|
||||
|
||||
func (t planTable) addCandidate(e *endpoint.Endpoint) {
|
||||
dnsName := normalizeDNSName(e.DNSName)
|
||||
if _, ok := t.rows[dnsName]; !ok {
|
||||
t.rows[dnsName] = make(map[string]*planTableRow)
|
||||
t.rows[dnsName] = make(map[string]map[string]*planTableRow)
|
||||
}
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier] = &planTableRow{}
|
||||
t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow)
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier].candidates = append(t.rows[dnsName][e.SetIdentifier].candidates, e)
|
||||
if _, ok := t.rows[dnsName][e.SetIdentifier][e.RecordType]; !ok {
|
||||
t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{}
|
||||
}
|
||||
t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates = append(t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates, e)
|
||||
}
|
||||
|
||||
func (c *Changes) HasChanges() bool {
|
||||
@ -147,24 +153,26 @@ func (p *Plan) Calculate() *Plan {
|
||||
changes := &Changes{}
|
||||
|
||||
for _, topRow := range t.rows {
|
||||
for _, row := range topRow {
|
||||
if row.current == nil { // dns name not taken
|
||||
changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
changes.Delete = append(changes.Delete, row.current)
|
||||
}
|
||||
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
if row.current != nil && len(row.candidates) > 0 { // dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
changes.UpdateNew = append(changes.UpdateNew, update)
|
||||
changes.UpdateOld = append(changes.UpdateOld, row.current)
|
||||
for _, midRow := range topRow {
|
||||
for _, row := range midRow {
|
||||
if row.current == nil { // dns name not taken
|
||||
changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates))
|
||||
}
|
||||
if row.current != nil && len(row.candidates) == 0 {
|
||||
changes.Delete = append(changes.Delete, row.current)
|
||||
}
|
||||
|
||||
// TODO: allows record type change, which might not be supported by all dns providers
|
||||
if row.current != nil && len(row.candidates) > 0 { // dns name is taken
|
||||
update := t.resolver.ResolveUpdate(row.current, row.candidates)
|
||||
// compare "update" to "current" to figure out if actual update is required
|
||||
if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) {
|
||||
inheritOwner(row.current, update)
|
||||
changes.UpdateNew = append(changes.UpdateNew, update)
|
||||
changes.UpdateOld = append(changes.UpdateOld, row.current)
|
||||
}
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +189,7 @@ func (p *Plan) Calculate() *Plan {
|
||||
Current: p.Current,
|
||||
Desired: p.Desired,
|
||||
Changes: changes,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
return plan
|
||||
|
@ -35,6 +35,9 @@ type PlanTestSuite struct {
|
||||
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
|
||||
@ -106,6 +109,30 @@ func (suite *PlanTestSuite) SetupTest() {
|
||||
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{"1.1.1.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"},
|
||||
@ -438,9 +465,9 @@ func (suite *PlanTestSuite) TestIdempotency() {
|
||||
func (suite *PlanTestSuite) TestDifferentTypes() {
|
||||
current := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooA5}
|
||||
expectedCreate := []*endpoint.Endpoint{}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.fooA5}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooA5}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooV2Cname}
|
||||
expectedDelete := []*endpoint.Endpoint{}
|
||||
|
||||
p := &Plan{
|
||||
@ -544,52 +571,6 @@ func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() {
|
||||
validateEntries(suite.T(), changes.Delete, expectedDelete)
|
||||
}
|
||||
|
||||
// TODO: remove once multiple-target per endpoint is supported
|
||||
func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() {
|
||||
current := []*endpoint.Endpoint{suite.fooV3CnameSameResource, suite.bar192A}
|
||||
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource}
|
||||
expectedCreate := []*endpoint.Endpoint{}
|
||||
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV3CnameSameResource}
|
||||
expectedUpdateNew := []*endpoint.Endpoint{suite.fooV1Cname}
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO: remove once multiple-target per endpoint is supported
|
||||
func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() {
|
||||
current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A}
|
||||
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource}
|
||||
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) TestMultipleRecordsSameNameDifferentSetIdentifier() {
|
||||
current := []*endpoint.Endpoint{suite.multiple1}
|
||||
desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3}
|
||||
@ -695,6 +676,39 @@ func (suite *PlanTestSuite) TestMissing() {
|
||||
validateEntries(suite.T(), changes.Create, expectedCreate)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestAAAARecords() {
|
||||
|
||||
current := []*endpoint.Endpoint{}
|
||||
desired := []*endpoint.Endpoint{suite.fooAAAA}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.fooAAAA}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestDualStackRecords() {
|
||||
current := []*endpoint.Endpoint{}
|
||||
desired := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
||||
expectedCreate := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func TestPlan(t *testing.T) {
|
||||
suite.Run(t, new(PlanTestSuite))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user