docs/contributing/source-wrappers.md
In ExternalDNS, a Source is a component responsible for discovering DNS records from Kubernetes resources (e.g., Ingress, Service, Gateway, etc.).
Source Wrappers are middleware-like components that sit between the source and the plan generation. They extend or modify the behavior of the original sources by transforming, filtering, or enriching the DNS records before they're processed by the planner and provider.
Wrappers solve these key challenges:
FakeSource or wrappers for dry-runs or simulations.| Wrapper | Purpose | Use Case |
|---|---|---|
MultiSource | Combine multiple sources. | Aggregate Ingress, Service, etc. |
DedupSource | Remove duplicate DNS records. | Avoid duplicate records from sources. |
TargetFilterSource | Include/exclude targets based on CIDRs. | Exclude internal IPs. |
NAT64Source | Add NAT64-prefixed AAAA records. | Support IPv6 with NAT64. |
PostProcessor | Add records post-processing. | Configure TTL, filter provider-specific properties. |
PTRSource | Generate PTR records from A/AAAA. | Automatic reverse DNS entries. |
TargetFilterSourceFilters targets (e.g. IPs or hostnames) based on inclusion or exclusion rules.
๐ Use case: Only publish public IPs, exclude test environments.
--target-net-filter=192.168.0.0/16
--exclude-target-nets=10.0.0.0/8
NAT64SourceConverts IPv4 targets to IPv6 using NAT64 prefixes.
๐ Use case: Publish AAAA records for IPv6-only clients in NAT64 environments.
--nat64-prefix=64:ff9b::/96
PostProcessorApplies post-processing to all endpoints after they are collected from sources.
๐ Use case
ProviderSpecific properties to retain only those belonging to the configured provider (e.g. aws/evaluate-target-health when provider is aws). Properties with no provider prefix (e.g. alias) are considered provider-agnostic and are always retained.alias=true provider-specific property on CNAME endpoints when --prefer-alias is enabled, signalling providers that support ALIAS records (e.g. PowerDNS, AWS) to use them instead of CNAMEs. Per-resource annotations already present are not overwritten.--min-ttl=60s
--provider=aws
--prefer-alias
Wrappers wrap a Source and implement the same Source interface (e.g., Endpoints(ctx)).
They typically follow this pattern:
package wrappers
type myWrapper struct {
next source.Source
}
func (m *myWrapper) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
eps, err := m.next.Endpoints(ctx)
if err != nil {
return nil, err
}
// Modify, filter, or enrich endpoints as needed
return eps, nil
}
// AddEventHandler must be implemented to satisfy the source.Source interface.
func (m *myWrapper) AddEventHandler(ctx context.Context, handler func()) {
log.Debugf("myWrapper: adding event handler")
m.next.AddEventHandler(ctx, handler)
}
This allows wrappers to be stacked or composed together.
Wrappers are often composed like this:
source := NewMultiSource(actualSources, defaultTargets)
source = NewDedupSource(source)
source = NewNAT64Source(source, cfg.NAT64Networks)
source = NewTargetFilterSource(source, targetFilter)
source = NewPostProcessor(source, WithTTL(minTTL), WithPostProcessorPreferAlias(preferAlias))
source = NewPTRSource(source, createPTR)
Each wrapper processes the output of the previous one.
sequenceDiagram
participant ExternalDNS
participant Source
participant Wrapper
participant DedupWrapper as DedupSource
participant Provider
participant Plan
ExternalDNS->>Source: Initialize source (e.g. Ingress, Service)
Source-->>ExternalDNS: Implements Source interface
ExternalDNS->>Wrapper: Wrap with decorators (e.g. dedup, filters)
Wrapper->>DedupWrapper: Compose with DedupSource
DedupWrapper-->>Wrapper: Return enriched Source
Wrapper-->>ExternalDNS: Return final wrapped Source
ExternalDNS->>Plan: Generate plan from Source
Plan->>Wrapper: Call Endpoints(ctx)
Wrapper->>DedupWrapper: Call Endpoints(ctx)
DedupWrapper->>Source: Call Endpoints(ctx)
Source-->>DedupWrapper: Return []*Endpoint
DedupWrapper-->>Wrapper: Return de-duplicated []*Endpoint
Wrapper-->>Wrapper: PostProcessor: set TTL, alias
Wrapper-->>Wrapper: PTRSource: generate PTR from A/AAAA
Wrapper-->>Plan: Return transformed []*Endpoint
ExternalDNS->>Provider: ApplyChanges(plan)
Provider-->>ExternalDNS: Sync DNS records