Skip to content

Commit

Permalink
Add --exclude-record-types flag
Browse files Browse the repository at this point in the history
  • Loading branch information
johngmyers committed Aug 12, 2023
1 parent b420a32 commit ad8d30a
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 51 deletions.
5 changes: 4 additions & 1 deletion controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ type Controller struct {
nextRunAt time.Time
// The nextRunAtMux is for atomic updating of nextRunAt
nextRunAtMux sync.Mutex
// DNS record types that will be considered for management
// MangedRecordTypes are DNS record types that will be considered for management.
ManagedRecordTypes []string
// ExcludeRecordTypes are DNS record types that will be excluded from management.
ExcludeRecordTypes []string
// MinEventSyncInterval is used as window for batching events
MinEventSyncInterval time.Duration
}
Expand Down Expand Up @@ -222,6 +224,7 @@ func (c *Controller) RunOnce(ctx context.Context) error {
Desired: endpoints,
DomainFilter: endpoint.MatchAllDomainFilters{c.DomainFilter, c.Registry.GetDomainFilter()},
ManagedRecords: c.ManagedRecordTypes,
ExcludeRecords: c.ExcludeRecordTypes,
}

plan = plan.Calculate()
Expand Down
5 changes: 3 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,11 @@ func main() {
if cfg.AWSDynamoDBRegion != "" {
config = config.WithRegion(cfg.AWSDynamoDBRegion)
}
r, err = registry.NewDynamoDBRegistry(p, cfg.TXTOwnerID, dynamodb.New(awsSession, config), cfg.AWSDynamoDBTable, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, []byte(cfg.TXTEncryptAESKey), cfg.TXTCacheInterval)
r, err = registry.NewDynamoDBRegistry(p, cfg.TXTOwnerID, dynamodb.New(awsSession, config), cfg.AWSDynamoDBTable, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, []byte(cfg.TXTEncryptAESKey), cfg.TXTCacheInterval)
case "noop":
r, err = registry.NewNoopRegistry(p)
case "txt":
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey))
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey))
case "aws-sd":
r, err = registry.NewAWSSDRegistry(p.(*awssd.AWSSDProvider), cfg.TXTOwnerID)
default:
Expand All @@ -428,6 +428,7 @@ func main() {
Interval: cfg.Interval,
DomainFilter: domainFilter,
ManagedRecordTypes: cfg.ManagedDNSRecordTypes,
ExcludeRecordTypes: cfg.ExcludeDNSRecordTypes,
MinEventSyncInterval: cfg.MinEventSyncInterval,
}

Expand Down
5 changes: 4 additions & 1 deletion pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ type Config struct {
TransIPPrivateKeyFile string
DigitalOceanAPIPageSize int
ManagedDNSRecordTypes []string
ExcludeDNSRecordTypes []string
GoDaddyAPIKey string `secure:"yes"`
GoDaddySecretKey string `secure:"yes"`
GoDaddyTTL int64
Expand Down Expand Up @@ -339,6 +340,7 @@ var defaultConfig = &Config{
TransIPPrivateKeyFile: "",
DigitalOceanAPIPageSize: 50,
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
ExcludeDNSRecordTypes: []string{},
GoDaddyAPIKey: "",
GoDaddySecretKey: "",
GoDaddyTTL: 600,
Expand Down Expand Up @@ -434,7 +436,8 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion)
app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind)
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: CNAME, A, AAAA, NS").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: A, AAAA, CNAME, NS, SRV, TXT)").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
app.Flag("exclude-record-types", "Record types to exclude from management; specify multiple times to exclude many; (optional)").Default().StringsVar(&cfg.ExcludeDNSRecordTypes)
app.Flag("default-targets", "Set globally default host/IP that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets)
app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter)
app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets)
Expand Down
19 changes: 13 additions & 6 deletions plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ type Plan struct {
Changes *Changes
// DomainFilter matches DNS names
DomainFilter endpoint.DomainFilterInterface
// DNS record types that will be considered for management
// ManagedRecords are DNS record types that will be considered for management.
ManagedRecords []string
// ExcludeRecords are DNS record types that will be excluded from management.
ExcludeRecords []string
}

// Changes holds lists of actions to be executed by dns providers
Expand Down Expand Up @@ -140,10 +142,10 @@ func (p *Plan) Calculate() *Plan {
p.DomainFilter = endpoint.MatchAllDomainFilters(nil)
}

for _, current := range filterRecordsForPlan(p.Current, p.DomainFilter, p.ManagedRecords) {
for _, current := range filterRecordsForPlan(p.Current, p.DomainFilter, p.ManagedRecords, p.ExcludeRecords) {
t.addCurrent(current)
}
for _, desired := range filterRecordsForPlan(p.Desired, p.DomainFilter, p.ManagedRecords) {
for _, desired := range filterRecordsForPlan(p.Desired, p.DomainFilter, p.ManagedRecords, p.ExcludeRecords) {
t.addCandidate(desired)
}

Expand Down Expand Up @@ -232,7 +234,7 @@ func (p *Plan) shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint)
// Per RFC 1034, CNAME records conflict with all other records - it is the
// only record with this property. The behavior of the planner may need to be
// made more sophisticated to codify this.
func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, managedRecords []string) []*endpoint.Endpoint {
func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, managedRecords, excludeRecords []string) []*endpoint.Endpoint {
filtered := []*endpoint.Endpoint{}

for _, record := range records {
Expand All @@ -241,7 +243,7 @@ func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.Do
log.Debugf("ignoring record %s that does not match domain filter", record.DNSName)
continue
}
if IsManagedRecord(record.RecordType, managedRecords) {
if IsManagedRecord(record.RecordType, managedRecords, excludeRecords) {
filtered = append(filtered, record)
}
}
Expand Down Expand Up @@ -284,7 +286,12 @@ func CompareBoolean(defaultValue bool, name, current, previous string) bool {
return v1 == v2
}

func IsManagedRecord(record string, managedRecords []string) bool {
func IsManagedRecord(record string, managedRecords, excludeRecords []string) bool {
for _, r := range excludeRecords {
if record == r {
return false
}
}
for _, r := range managedRecords {
if record == r {
return true
Expand Down
23 changes: 23 additions & 0 deletions plan/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,29 @@ func (suite *PlanTestSuite) TestIgnoreTXT() {
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}
Expand Down
6 changes: 4 additions & 2 deletions registry/dynamodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type DynamoDBRegistry struct {
mapper nameMapper
wildcardReplacement string
managedRecordTypes []string
excludeRecordTypes []string
txtEncryptAESKey []byte

// cache the dynamodb records owned by us.
Expand All @@ -68,7 +69,7 @@ type DynamoDBRegistry struct {
const dynamodbAttributeMigrate = "dynamodb/needs-migration"

// NewDynamoDBRegistry returns a new DynamoDBRegistry object.
func NewDynamoDBRegistry(provider provider.Provider, ownerID string, dynamodbAPI DynamoDBAPI, table string, txtPrefix, txtSuffix, txtWildcardReplacement string, managedRecordTypes []string, txtEncryptAESKey []byte, cacheInterval time.Duration) (*DynamoDBRegistry, error) {
func NewDynamoDBRegistry(provider provider.Provider, ownerID string, dynamodbAPI DynamoDBAPI, table string, txtPrefix, txtSuffix, txtWildcardReplacement string, managedRecordTypes, excludeRecordTypes []string, txtEncryptAESKey []byte, cacheInterval time.Duration) (*DynamoDBRegistry, error) {
if ownerID == "" {
return nil, errors.New("owner id cannot be empty")
}
Expand All @@ -95,6 +96,7 @@ func NewDynamoDBRegistry(provider provider.Provider, ownerID string, dynamodbAPI
mapper: mapper,
wildcardReplacement: txtWildcardReplacement,
managedRecordTypes: managedRecordTypes,
excludeRecordTypes: excludeRecordTypes,
txtEncryptAESKey: txtEncryptAESKey,
cacheInterval: cacheInterval,
}, nil
Expand Down Expand Up @@ -190,7 +192,7 @@ func (im *DynamoDBRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint,
}

// Remove any unused TXT ownership records owned by us
if len(txtRecordsMap) > 0 && !plan.IsManagedRecord(endpoint.RecordTypeTXT, im.managedRecordTypes) {
if len(txtRecordsMap) > 0 && !plan.IsManagedRecord(endpoint.RecordTypeTXT, im.managedRecordTypes, im.excludeRecordTypes) {
log.Infof("Old TXT ownership records will not be deleted because \"TXT\" is not in the set of managed record types.")
}
for _, record := range txtRecordsMap {
Expand Down
24 changes: 12 additions & 12 deletions registry/dynamodb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,31 @@ import (
func TestDynamoDBRegistryNew(t *testing.T) {
api, p := newDynamoDBAPIStub(t, nil)

_, err := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, []byte(""), time.Hour)
_, err := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, []string{}, []byte(""), time.Hour)
require.NoError(t, err)

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "testPrefix", "", "", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "testPrefix", "", "", []string{}, []string{}, []byte(""), time.Hour)
require.NoError(t, err)

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "testSuffix", "", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "testSuffix", "", []string{}, []string{}, []byte(""), time.Hour)
require.NoError(t, err)

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "testWildcard", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "testWildcard", []string{}, []string{}, []byte(""), time.Hour)
require.NoError(t, err)

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "testWildcard", []string{}, []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^"), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "testWildcard", []string{}, []string{}, []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^"), time.Hour)
require.NoError(t, err)

_, err = NewDynamoDBRegistry(p, "", api, "test-table", "", "", "", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "", api, "test-table", "", "", "", []string{}, []string{}, []byte(""), time.Hour)
require.EqualError(t, err, "owner id cannot be empty")

_, err = NewDynamoDBRegistry(p, "test-owner", api, "", "", "", "", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "", "", "", "", []string{}, []string{}, []byte(""), time.Hour)
require.EqualError(t, err, "table cannot be empty")

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^x"), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, []string{}, []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^x"), time.Hour)
require.EqualError(t, err, "the AES Encryption key must have a length of 32 bytes")

_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "testPrefix", "testSuffix", "", []string{}, []byte(""), time.Hour)
_, err = NewDynamoDBRegistry(p, "test-owner", api, "test-table", "testPrefix", "testSuffix", "", []string{}, []string{}, []byte(""), time.Hour)
require.EqualError(t, err, "txt-prefix and txt-suffix are mutually exclusive")
}

Expand Down Expand Up @@ -112,7 +112,7 @@ func TestDynamoDBRegistryRecordsBadTable(t *testing.T) {
api, p := newDynamoDBAPIStub(t, nil)
tc.setup(&api.tableDescription)

r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, nil, time.Hour)
r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "", "", "", []string{}, []string{}, nil, time.Hour)

_, err := r.Records(context.Background())
assert.EqualError(t, err, tc.expected)
Expand Down Expand Up @@ -198,7 +198,7 @@ func TestDynamoDBRegistryRecords(t *testing.T) {
},
}

r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "txt.", "", "", []string{}, nil, time.Hour)
r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "txt.", "", "", []string{}, []string{}, nil, time.Hour)
_ = p.(*wrappedProvider).Provider.ApplyChanges(context.Background(), &plan.Changes{
Create: []*endpoint.Endpoint{
endpoint.NewEndpoint("migrate.test-zone.example.org", endpoint.RecordTypeA, "3.3.3.3").WithSetIdentifier("set-3"),
Expand Down Expand Up @@ -920,7 +920,7 @@ func TestDynamoDBRegistryApplyChanges(t *testing.T) {

ctx := context.Background()

r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "txt.", "", "", []string{}, nil, time.Hour)
r, _ := NewDynamoDBRegistry(p, "test-owner", api, "test-table", "txt.", "", "", []string{}, []string{}, nil, time.Hour)
_, err := r.Records(ctx)
require.Nil(t, err)

Expand Down
6 changes: 4 additions & 2 deletions registry/txt.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@ type TXTRegistry struct {
wildcardReplacement string

managedRecordTypes []string
excludeRecordTypes []string

// encrypt text records
txtEncryptEnabled bool
txtEncryptAESKey []byte
}

// NewTXTRegistry returns new TXTRegistry object
func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes []string, txtEncryptEnabled bool, txtEncryptAESKey []byte) (*TXTRegistry, error) {
func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes, excludeRecordTypes []string, txtEncryptEnabled bool, txtEncryptAESKey []byte) (*TXTRegistry, error) {
if ownerID == "" {
return nil, errors.New("owner id cannot be empty")
}
Expand All @@ -90,6 +91,7 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st
cacheInterval: cacheInterval,
wildcardReplacement: txtWildcardReplacement,
managedRecordTypes: managedRecordTypes,
excludeRecordTypes: excludeRecordTypes,
txtEncryptEnabled: txtEncryptEnabled,
txtEncryptAESKey: txtEncryptAESKey,
}, nil
Expand Down Expand Up @@ -183,7 +185,7 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error
// Handle the migration of TXT records created before the new format (introduced in v0.12.0).
// The migration is done for the TXT records owned by this instance only.
if len(txtRecordsMap) > 0 && ep.Labels[endpoint.OwnerLabelKey] == im.ownerID {
if plan.IsManagedRecord(ep.RecordType, im.managedRecordTypes) {
if plan.IsManagedRecord(ep.RecordType, im.managedRecordTypes, im.excludeRecordTypes) {
// Get desired TXT records and detect the missing ones
desiredTXTs := im.generateTXTRecord(ep)
for _, desiredTXT := range desiredTXTs {
Expand Down
Loading

0 comments on commit ad8d30a

Please sign in to comment.