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 subdomain filter to AWS provider #1375

Closed
wants to merge 15 commits into from
4 changes: 4 additions & 0 deletions docs/tutorials/aws.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ Annotations which are specific to AWS.

`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. To make the target an alias, the ingress needs to be configured correctly as described in [the docs](./nginx-ingress.md#with-a-separate-tcp-load-balancer). In particular, the argument `--publish-service=default/nginx-ingress-controller` has to be set on the `nginx-ingress-controller` container. If one uses the `nginx-ingress` Helm chart, this flag can be set with the `controller.publishService.enabled` configuration option.

### subdomainFilter

`subdomainFilter` allows only domains matched by the subdomainfilter to be created.

## Verify ExternalDNS works (Ingress example)

Create an ingress resource manifest file.
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func main() {
zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
subdomainFilter := provider.NewDomainFilter(cfg.SubdomainFilter)

var p provider.Provider
switch cfg.Provider {
Expand All @@ -132,6 +133,7 @@ func main() {
ZoneIDFilter: zoneIDFilter,
ZoneTypeFilter: zoneTypeFilter,
ZoneTagFilter: zoneTagFilter,
SubdomainFilter: subdomainFilter,
BatchChangeSize: cfg.AWSBatchChangeSize,
BatchChangeInterval: cfg.AWSBatchChangeInterval,
EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth,
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Config struct {
GoogleBatchChangeSize int
GoogleBatchChangeInterval time.Duration
DomainFilter []string
SubdomainFilter []string
ExcludeDomains []string
ZoneIDFilter []string
AlibabaCloudConfigFile string
Expand Down Expand Up @@ -156,6 +157,7 @@ var defaultConfig = &Config{
GoogleBatchChangeSize: 1000,
GoogleBatchChangeInterval: time.Second,
DomainFilter: []string{},
SubdomainFilter: []string{},
ExcludeDomains: []string{},
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
AWSZoneType: "",
Expand Down Expand Up @@ -300,6 +302,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
app.Flag("subdomain-filter", "Allow only changes to specific subdomain").Default("").StringsVar(&cfg.SubdomainFilter)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
app.Flag("google-batch-change-size", "When using the Google provider, set the maximum number of changes that will be applied in each batch.").Default(strconv.Itoa(defaultConfig.GoogleBatchChangeSize)).IntVar(&cfg.GoogleBatchChangeSize)
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/externaldns/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var (
GoogleBatchChangeInterval: time.Second,
DomainFilter: []string{""},
ExcludeDomains: []string{""},
SubdomainFilter: []string{""},
ZoneIDFilter: []string{""},
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
AWSZoneType: "",
Expand Down Expand Up @@ -112,6 +113,7 @@ var (
GoogleBatchChangeSize: 100,
GoogleBatchChangeInterval: time.Second * 2,
DomainFilter: []string{"example.org", "company.com"},
SubdomainFilter: []string{"foo.example.org"},
ExcludeDomains: []string{"xapi.example.org", "xapi.company.com"},
ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"},
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
Expand Down Expand Up @@ -241,6 +243,7 @@ func TestParseFlags(t *testing.T) {
"--exclude-domains=xapi.company.com",
"--zone-id-filter=/hostedzone/ZTST1",
"--zone-id-filter=/hostedzone/ZTST2",
"--subdomain-filter=foo.example.org",
"--aws-zone-type=private",
"--aws-zone-tags=tag=foo",
"--aws-assume-role=some-other-role",
Expand Down Expand Up @@ -314,6 +317,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
"EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com",
"EXTERNAL_DNS_SUBDOMAIN_FILTER": "foo.example.org",
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
"EXTERNAL_DNS_PDNS_API_KEY": "some-secret-key",
"EXTERNAL_DNS_PDNS_TLS_ENABLED": "1",
Expand Down
22 changes: 20 additions & 2 deletions provider/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ type AWSProvider struct {
zoneTypeFilter ZoneTypeFilter
// filter hosted zones by tags
zoneTagFilter ZoneTagFilter
preferCNAME bool
// only allow changes to specified subdomain and its subdomains
subdomainFilter DomainFilter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this not be changed to SubdomainFilter instead of DomainFilter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subdomain filter is still a domain filter and the domain filtering provided by DomainFilter will be sufficient. However, I can add a SubdomainFilter type for readability and future changes. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please, just thought it would be better to divide it because of readability and future changes as well.

preferCNAME bool
}

// AWSConfig contains configuration to create a new AWS provider.
Expand All @@ -133,6 +135,7 @@ type AWSConfig struct {
ZoneIDFilter ZoneIDFilter
ZoneTypeFilter ZoneTypeFilter
ZoneTagFilter ZoneTagFilter
SubdomainFilter DomainFilter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here DomainFilter -> SubDomainFilter?

BatchChangeSize int
BatchChangeInterval time.Duration
EvaluateTargetHealth bool
Expand Down Expand Up @@ -174,6 +177,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) {
zoneIDFilter: awsConfig.ZoneIDFilter,
zoneTypeFilter: awsConfig.ZoneTypeFilter,
zoneTagFilter: awsConfig.ZoneTagFilter,
subdomainFilter: awsConfig.SubdomainFilter,
batchChangeSize: awsConfig.BatchChangeSize,
batchChangeInterval: awsConfig.BatchChangeInterval,
evaluateTargetHealth: awsConfig.EvaluateTargetHealth,
Expand Down Expand Up @@ -401,8 +405,10 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes []*route53.Chan
return nil
}

filteredChangesBySubdomains := filteredChangesBySubdomains(changes, p)

// separate into per-zone change sets to be passed to the API.
changesByZone := changesByZone(zones, changes)
changesByZone := changesByZone(zones, filteredChangesBySubdomains)
if len(changesByZone) == 0 {
log.Info("All records are already up to date, there are no changes for the matching hosted zones")
}
Expand Down Expand Up @@ -651,6 +657,18 @@ func sortChangesByActionNameType(cs []*route53.Change) []*route53.Change {
return cs
}

func filteredChangesBySubdomains(changeSet []*route53.Change, p *AWSProvider) []*route53.Change {
changes := []*route53.Change{}

for _, c := range changeSet {
hostname := aws.StringValue(c.ResourceRecordSet.Name)
if p.subdomainFilter.Match(hostname) {
changes = append(changes, c)
}
}
return changes
}

// changesByZone separates a multi-zone change into a single change per zone.
func changesByZone(zones map[string]*route53.HostedZone, changeSet []*route53.Change) map[string][]*route53.Change {
changes := make(map[string][]*route53.Change)
Expand Down
Loading