Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --exclude-record-types flag #3748

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,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 @@ -227,6 +229,7 @@ func (c *Controller) RunOnce(ctx context.Context) error {
Desired: endpoints,
DomainFilter: endpoint.MatchAllDomainFilters{&c.DomainFilter, &registryFilter},
ManagedRecords: c.ManagedRecordTypes,
ExcludeRecords: c.ExcludeRecordTypes,
OwnerID: c.Registry.OwnerID(),
}

Expand Down
5 changes: 3 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,11 +416,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 @@ -443,6 +443,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 @@ -194,6 +194,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 @@ -342,6 +343,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 @@ -437,7 +439,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.MatchAllDomainFilters
// 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
// OwnerID of records to manage
OwnerID string
}
Expand Down Expand Up @@ -170,10 +172,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 @@ -313,7 +315,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.MatchAllDomainFilters, managedRecords []string) []*endpoint.Endpoint {
func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.MatchAllDomainFilters, managedRecords, excludeRecords []string) []*endpoint.Endpoint {
filtered := []*endpoint.Endpoint{}

for _, record := range records {
Expand All @@ -322,7 +324,7 @@ func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.Ma
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 @@ -365,7 +367,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 @@ -681,6 +681,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 @@ -194,7 +196,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 @@ -51,14 +51,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 @@ -84,6 +85,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 @@ -181,7 +183,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
Loading