mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36: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)
|
*df = NewRegexDomainFilter(include, exclude)
|
||||||
return nil
|
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
|
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
|
AWSPreferCNAME bool
|
||||||
AWSZoneCacheDuration time.Duration
|
AWSZoneCacheDuration time.Duration
|
||||||
AWSSDServiceCleanup bool
|
AWSSDServiceCleanup bool
|
||||||
|
AWSZoneMatchParent bool
|
||||||
AWSDynamoDBRegion string
|
AWSDynamoDBRegion string
|
||||||
AWSDynamoDBTable string
|
AWSDynamoDBTable string
|
||||||
AzureConfigFile string
|
AzureConfigFile string
|
||||||
@ -257,6 +258,7 @@ var defaultConfig = &Config{
|
|||||||
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
|
||||||
AWSZoneType: "",
|
AWSZoneType: "",
|
||||||
AWSZoneTagFilter: []string{},
|
AWSZoneTagFilter: []string{},
|
||||||
|
AWSZoneMatchParent: false,
|
||||||
AWSAssumeRole: "",
|
AWSAssumeRole: "",
|
||||||
AWSAssumeRoleExternalID: "",
|
AWSAssumeRoleExternalID: "",
|
||||||
AWSBatchChangeSize: 1000,
|
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-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-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-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("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-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)
|
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
|
zoneTypeFilter provider.ZoneTypeFilter
|
||||||
// filter hosted zones by tags
|
// filter hosted zones by tags
|
||||||
zoneTagFilter provider.ZoneTagFilter
|
zoneTagFilter provider.ZoneTagFilter
|
||||||
preferCNAME bool
|
// extend filter for sub-domains in the zone (e.g. first.us-east-1.example.com)
|
||||||
zonesCache *zonesListCache
|
zoneMatchParent bool
|
||||||
|
preferCNAME bool
|
||||||
|
zonesCache *zonesListCache
|
||||||
// queue for collecting changes to submit them in the next iteration, but after all other changes
|
// queue for collecting changes to submit them in the next iteration, but after all other changes
|
||||||
failedChangesQueue map[string]Route53Changes
|
failedChangesQueue map[string]Route53Changes
|
||||||
}
|
}
|
||||||
@ -251,6 +253,7 @@ type AWSConfig struct {
|
|||||||
ZoneIDFilter provider.ZoneIDFilter
|
ZoneIDFilter provider.ZoneIDFilter
|
||||||
ZoneTypeFilter provider.ZoneTypeFilter
|
ZoneTypeFilter provider.ZoneTypeFilter
|
||||||
ZoneTagFilter provider.ZoneTagFilter
|
ZoneTagFilter provider.ZoneTagFilter
|
||||||
|
ZoneMatchParent bool
|
||||||
BatchChangeSize int
|
BatchChangeSize int
|
||||||
BatchChangeInterval time.Duration
|
BatchChangeInterval time.Duration
|
||||||
EvaluateTargetHealth bool
|
EvaluateTargetHealth bool
|
||||||
@ -267,6 +270,7 @@ func NewAWSProvider(awsConfig AWSConfig, client Route53API) (*AWSProvider, error
|
|||||||
zoneIDFilter: awsConfig.ZoneIDFilter,
|
zoneIDFilter: awsConfig.ZoneIDFilter,
|
||||||
zoneTypeFilter: awsConfig.ZoneTypeFilter,
|
zoneTypeFilter: awsConfig.ZoneTypeFilter,
|
||||||
zoneTagFilter: awsConfig.ZoneTagFilter,
|
zoneTagFilter: awsConfig.ZoneTagFilter,
|
||||||
|
zoneMatchParent: awsConfig.ZoneMatchParent,
|
||||||
batchChangeSize: awsConfig.BatchChangeSize,
|
batchChangeSize: awsConfig.BatchChangeSize,
|
||||||
batchChangeInterval: awsConfig.BatchChangeInterval,
|
batchChangeInterval: awsConfig.BatchChangeInterval,
|
||||||
evaluateTargetHealth: awsConfig.EvaluateTargetHealth,
|
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)) {
|
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
|
// Only fetch tags if a tag filter was specified
|
||||||
|
Loading…
Reference in New Issue
Block a user