mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 09:06:58 +02:00
feat(aws-provider): create flag to support sub-domains match parent
The current implementation of external-dns from sig-external-dns does not support domain filtering (--domain-filter) for sub-domains on Route53, such as test.sub-domain.domain.com. The function MatchParent was recently removed from the base code, but it is still necessary for this purpose. An example of a use case for this support is having a cluster per hosted zone with a hundred ingress related to that zone with different variants of sub-domains. With the matchParent function and zone-match-parent flag, external-dns will now support an extended automatic match for sub-domains.
This commit is contained in:
parent
67939d0b41
commit
70835ab7bd
@ -199,3 +199,24 @@ func (df *DomainFilter) UnmarshalJSON(b []byte) error {
|
||||
*df = NewRegexDomainFilter(include, exclude)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (df DomainFilter) MatchParent(domain string) bool {
|
||||
if matchFilter(df.exclude, domain, false) {
|
||||
return false
|
||||
}
|
||||
if len(df.Filters) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
|
||||
for _, filter := range df.Filters {
|
||||
if filter == "" || strings.HasPrefix(filter, ".") {
|
||||
// We don't check parents if the filter is prefixed with "."
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(filter, "."+strippedDomain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -668,3 +668,104 @@ func deserialize[T any](t *testing.T, serialized map[string]T) DomainFilter {
|
||||
|
||||
return deserialized
|
||||
}
|
||||
|
||||
func TestDomainFilterMatchParent(t *testing.T) {
|
||||
parentMatchTests := []domainFilterTest{
|
||||
{
|
||||
[]string{"a.example.com."},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
true,
|
||||
map[string][]string{
|
||||
"include": {"a.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{" a.example.com "},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
true,
|
||||
map[string][]string{
|
||||
"include": {"a.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{""},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
true,
|
||||
map[string][]string{},
|
||||
},
|
||||
{
|
||||
[]string{".a.example.com."},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
false,
|
||||
map[string][]string{
|
||||
"include": {".a.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{"a.example.com.", "b.example.com"},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
true,
|
||||
map[string][]string{
|
||||
"include": {"a.example.com", "b.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{"a.example.com"},
|
||||
[]string{},
|
||||
[]string{"b.example.com"},
|
||||
false,
|
||||
map[string][]string{
|
||||
"include": {"a.example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{"example.com"},
|
||||
[]string{},
|
||||
[]string{"example.com"},
|
||||
false,
|
||||
map[string][]string{
|
||||
"include": {"example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{"example.com"},
|
||||
[]string{},
|
||||
[]string{"anexample.com"},
|
||||
false,
|
||||
map[string][]string{
|
||||
"include": {"example.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{""},
|
||||
[]string{},
|
||||
[]string{""},
|
||||
true,
|
||||
map[string][]string{},
|
||||
},
|
||||
}
|
||||
for i, tt := range parentMatchTests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
domainFilter := NewDomainFilterWithExclusions(tt.domainFilter, tt.exclusions)
|
||||
|
||||
assertSerializes(t, domainFilter, tt.expectedSerialization)
|
||||
deserialized := deserialize(t, map[string][]string{
|
||||
"include": tt.domainFilter,
|
||||
"exclude": tt.exclusions,
|
||||
})
|
||||
|
||||
for _, domain := range tt.domains {
|
||||
assert.Equal(t, tt.expected, domainFilter.MatchParent(domain), "%v", domain)
|
||||
assert.Equal(t, tt.expected, domainFilter.MatchParent(domain+"."), "%v", domain+".")
|
||||
|
||||
assert.Equal(t, tt.expected, deserialized.MatchParent(domain), "deserialized %v", domain)
|
||||
assert.Equal(t, tt.expected, deserialized.MatchParent(domain+"."), "deserialized %v", domain+".")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ type Config struct {
|
||||
AWSPreferCNAME bool
|
||||
AWSZoneCacheDuration time.Duration
|
||||
AWSSDServiceCleanup bool
|
||||
AWSZoneMatchParent bool
|
||||
AWSDynamoDBRegion string
|
||||
AWSDynamoDBTable string
|
||||
AzureConfigFile string
|
||||
@ -257,6 +258,7 @@ var defaultConfig = &Config{
|
||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||
AWSZoneType: "",
|
||||
AWSZoneTagFilter: []string{},
|
||||
AWSZoneMatchParent: false,
|
||||
AWSAssumeRole: "",
|
||||
AWSAssumeRoleExternalID: "",
|
||||
AWSBatchChangeSize: 1000,
|
||||
@ -488,6 +490,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("aws-api-retries", "When using the AWS API, set the maximum number of retries before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries)
|
||||
app.Flag("aws-prefer-cname", "When using the AWS provider, prefer using CNAME instead of ALIAS (default: disabled)").BoolVar(&cfg.AWSPreferCNAME)
|
||||
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
|
||||
app.Flag("aws-zone-match-parent", "Expand limit possible target by sub-domains (default: disabled)").BoolVar(&cfg.AWSZoneMatchParent)
|
||||
app.Flag("aws-sd-service-cleanup", "When using the AWS CloudMap provider, delete empty Services without endpoints (default: disabled)").BoolVar(&cfg.AWSSDServiceCleanup)
|
||||
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure)").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
|
||||
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
||||
|
@ -239,8 +239,10 @@ type AWSProvider struct {
|
||||
zoneTypeFilter provider.ZoneTypeFilter
|
||||
// filter hosted zones by tags
|
||||
zoneTagFilter provider.ZoneTagFilter
|
||||
preferCNAME bool
|
||||
zonesCache *zonesListCache
|
||||
// extend filter for sub-domains in the zone (e.g. first.us-east-1.example.com)
|
||||
zoneMatchParent bool
|
||||
preferCNAME bool
|
||||
zonesCache *zonesListCache
|
||||
// queue for collecting changes to submit them in the next iteration, but after all other changes
|
||||
failedChangesQueue map[string]Route53Changes
|
||||
}
|
||||
@ -251,6 +253,7 @@ type AWSConfig struct {
|
||||
ZoneIDFilter provider.ZoneIDFilter
|
||||
ZoneTypeFilter provider.ZoneTypeFilter
|
||||
ZoneTagFilter provider.ZoneTagFilter
|
||||
ZoneMatchParent bool
|
||||
BatchChangeSize int
|
||||
BatchChangeInterval time.Duration
|
||||
EvaluateTargetHealth bool
|
||||
@ -267,6 +270,7 @@ func NewAWSProvider(awsConfig AWSConfig, client Route53API) (*AWSProvider, error
|
||||
zoneIDFilter: awsConfig.ZoneIDFilter,
|
||||
zoneTypeFilter: awsConfig.ZoneTypeFilter,
|
||||
zoneTagFilter: awsConfig.ZoneTagFilter,
|
||||
zoneMatchParent: awsConfig.ZoneMatchParent,
|
||||
batchChangeSize: awsConfig.BatchChangeSize,
|
||||
batchChangeInterval: awsConfig.BatchChangeInterval,
|
||||
evaluateTargetHealth: awsConfig.EvaluateTargetHealth,
|
||||
@ -301,7 +305,12 @@ func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53.HostedZone
|
||||
}
|
||||
|
||||
if !p.domainFilter.Match(aws.StringValue(zone.Name)) {
|
||||
continue
|
||||
if !p.zoneMatchParent {
|
||||
continue
|
||||
}
|
||||
if !p.domainFilter.MatchParent(aws.StringValue(zone.Name)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Only fetch tags if a tag filter was specified
|
||||
|
Loading…
Reference in New Issue
Block a user