Skip to content

Commit dfb64ae

Browse files
chore(source): reorganise sources and wrappers (#5598)
* chore(source): reorganise sources and wrappers Signed-off-by: ivan katliarchuk <[email protected]> * chore(source): reorganise sources and wrappers Signed-off-by: ivan katliarchuk <[email protected]> * chore(source): reorganise sources and wrappers Signed-off-by: ivan katliarchuk <[email protected]> --------- Signed-off-by: ivan katliarchuk <[email protected]>
1 parent 2e50ddb commit dfb64ae

File tree

11 files changed

+162
-37
lines changed

11 files changed

+162
-37
lines changed

controller/execute.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
log "github.com/sirupsen/logrus"
3434
"k8s.io/klog/v2"
3535

36+
"sigs.k8s.io/external-dns/source/wrappers"
37+
3638
"sigs.k8s.io/external-dns/endpoint"
3739
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
3840
"sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
@@ -423,11 +425,11 @@ func buildSource(ctx context.Context, cfg *externaldns.Config) (source.Source, e
423425
return nil, err
424426
}
425427
// Combine multiple sources into a single, deduplicated source.
426-
combinedSource := source.NewDedupSource(source.NewMultiSource(sources, sourceCfg.DefaultTargets, sourceCfg.ForceDefaultTargets))
428+
combinedSource := wrappers.NewDedupSource(wrappers.NewMultiSource(sources, sourceCfg.DefaultTargets, sourceCfg.ForceDefaultTargets))
427429
// Filter targets
428430
targetFilter := endpoint.NewTargetNetFilterWithExclusions(cfg.TargetNetFilter, cfg.ExcludeTargetNets)
429-
combinedSource = source.NewNAT64Source(combinedSource, cfg.NAT64Networks)
430-
combinedSource = source.NewTargetFilterSource(combinedSource, targetFilter)
431+
combinedSource = wrappers.NewNAT64Source(combinedSource, cfg.NAT64Networks)
432+
combinedSource = wrappers.NewTargetFilterSource(combinedSource, targetFilter)
431433
return combinedSource, nil
432434
}
433435

source/endpoints.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
8181
return endpoints
8282
}
8383

84+
func EndpointsForHostname(
85+
hostname string,
86+
targets endpoint.Targets,
87+
ttl endpoint.TTL,
88+
providerSpecific endpoint.ProviderSpecific,
89+
setIdentifier string,
90+
resource string,
91+
) []*endpoint.Endpoint {
92+
return endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier, resource)
93+
}
94+
8495
func EndpointTargetsFromServices(svcInformer coreinformers.ServiceInformer, namespace string, selector map[string]string) (endpoint.Targets, error) {
8596
targets := endpoint.Targets{}
8697

source/dedupsource.go renamed to source/wrappers/dedupsource.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,26 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
2121
"strings"
2222

2323
log "github.com/sirupsen/logrus"
2424

25+
"sigs.k8s.io/external-dns/source"
26+
2527
"sigs.k8s.io/external-dns/endpoint"
2628
)
2729

2830
// dedupSource is a Source that removes duplicate endpoints from its wrapped source.
2931
type dedupSource struct {
30-
source Source
32+
source source.Source
3133
}
3234

3335
// NewDedupSource creates a new dedupSource wrapping the provided Source.
34-
func NewDedupSource(source Source) Source {
36+
func NewDedupSource(source source.Source) source.Source {
3537
return &dedupSource{source: source}
3638
}
3739

source/dedupsource_test.go renamed to source/wrappers/dedupsource_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
2121
"testing"
2222

2323
"sigs.k8s.io/external-dns/endpoint"
2424
"sigs.k8s.io/external-dns/internal/testutils"
25+
"sigs.k8s.io/external-dns/source"
2526
)
2627

2728
// Validates that dedupSource is a Source
28-
var _ Source = &dedupSource{}
29+
var _ source.Source = &dedupSource{}
2930

3031
func TestDedup(t *testing.T) {
3132
t.Run("Endpoints", testDedupEndpoints)

source/multisource.go renamed to source/wrappers/multisource.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
2121
"strings"
2222

2323
"sigs.k8s.io/external-dns/endpoint"
24+
"sigs.k8s.io/external-dns/source"
2425

2526
log "github.com/sirupsen/logrus"
2627
)
2728

2829
// multiSource is a Source that merges the endpoints of its nested Sources.
2930
type multiSource struct {
30-
children []Source
31+
children []source.Source
3132
defaultTargets []string
3233
forceDefaultTargets bool
3334
}
@@ -48,20 +49,20 @@ func (ms *multiSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, err
4849
continue
4950
}
5051

51-
for i := range endpoints {
52-
hasSourceTargets := len(endpoints[i].Targets) > 0
52+
for _, ep := range endpoints {
53+
hasSourceTargets := len(ep.Targets) > 0
5354

5455
if ms.forceDefaultTargets || !hasSourceTargets {
55-
eps := endpointsForHostname(endpoints[i].DNSName, ms.defaultTargets, endpoints[i].RecordTTL, endpoints[i].ProviderSpecific, endpoints[i].SetIdentifier, "")
56-
for _, ep := range eps {
57-
ep.Labels = endpoints[i].Labels
56+
eps := source.EndpointsForHostname(ep.DNSName, ms.defaultTargets, ep.RecordTTL, ep.ProviderSpecific, ep.SetIdentifier, "")
57+
for _, e := range eps {
58+
e.Labels = ep.Labels
5859
}
5960
result = append(result, eps...)
6061
continue
6162
}
6263

63-
log.Warnf("Source provided targets for %q (%s), ignoring default targets [%s] due to new behavior. Use --force-default-targets to revert to old behavior.", endpoints[i].DNSName, endpoints[i].RecordType, strings.Join(ms.defaultTargets, ", "))
64-
result = append(result, endpoints[i])
64+
log.Warnf("Source provided targets for %q (%s), ignoring default targets [%s] due to new behavior. Use --force-default-targets to revert to old behavior.", ep.DNSName, ep.RecordType, strings.Join(ms.defaultTargets, ", "))
65+
result = append(result, ep)
6566
}
6667
}
6768

@@ -75,6 +76,6 @@ func (ms *multiSource) AddEventHandler(ctx context.Context, handler func()) {
7576
}
7677

7778
// NewMultiSource creates a new multiSource.
78-
func NewMultiSource(children []Source, defaultTargets []string, forceDefaultTargets bool) Source {
79+
func NewMultiSource(children []source.Source, defaultTargets []string, forceDefaultTargets bool) source.Source {
7980
return &multiSource{children: children, defaultTargets: defaultTargets, forceDefaultTargets: forceDefaultTargets}
8081
}

source/multisource_test.go renamed to source/wrappers/multisource_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/stretchr/testify/assert"
2525
"github.com/stretchr/testify/require"
26+
"sigs.k8s.io/external-dns/source"
2627

2728
"sigs.k8s.io/external-dns/endpoint"
2829
"sigs.k8s.io/external-dns/internal/testutils"
@@ -39,7 +40,7 @@ func TestMultiSource(t *testing.T) {
3940

4041
// testMultiSourceImplementsSource tests that multiSource is a valid Source.
4142
func testMultiSourceImplementsSource(t *testing.T) {
42-
assert.Implements(t, (*Source)(nil), new(multiSource))
43+
assert.Implements(t, (*source.Source)(nil), new(multiSource))
4344
}
4445

4546
// testMultiSourceEndpoints tests merged endpoints from children are returned.
@@ -78,7 +79,7 @@ func testMultiSourceEndpoints(t *testing.T) {
7879
t.Parallel()
7980

8081
// Prepare the nested mock sources.
81-
sources := make([]Source, 0, len(tc.nestedEndpoints))
82+
sources := make([]source.Source, 0, len(tc.nestedEndpoints))
8283

8384
// Populate the nested mock sources.
8485
for _, endpoints := range tc.nestedEndpoints {
@@ -116,7 +117,7 @@ func testMultiSourceEndpointsWithError(t *testing.T) {
116117
src.On("Endpoints").Return(nil, errSomeError)
117118

118119
// Create our object under test and get the endpoints.
119-
source := NewMultiSource([]Source{src}, nil, false)
120+
source := NewMultiSource([]source.Source{src}, nil, false)
120121

121122
// Get endpoints from our source.
122123
_, err := source.Endpoints(context.Background())
@@ -155,7 +156,7 @@ func testMultiSourceEndpointsDefaultTargets(t *testing.T) {
155156
src.On("Endpoints").Return(sourceEndpoints, nil)
156157

157158
// Test with forceDefaultTargets=false (default behavior)
158-
source := NewMultiSource([]Source{src}, defaultTargets, false)
159+
source := NewMultiSource([]source.Source{src}, defaultTargets, false)
159160

160161
endpoints, err := source.Endpoints(context.Background())
161162
require.NoError(t, err)
@@ -185,7 +186,7 @@ func testMultiSourceEndpointsDefaultTargets(t *testing.T) {
185186
src.On("Endpoints").Return(sourceEndpoints, nil)
186187

187188
// Test with forceDefaultTargets=false (default behavior)
188-
source := NewMultiSource([]Source{src}, defaultTargets, false)
189+
source := NewMultiSource([]source.Source{src}, defaultTargets, false)
189190

190191
endpoints, err := source.Endpoints(context.Background())
191192
require.NoError(t, err)
@@ -223,7 +224,7 @@ func testMultiSourceEndpointsDefaultTargets(t *testing.T) {
223224
src.On("Endpoints").Return(sourceEndpoints, nil)
224225

225226
// Test with forceDefaultTargets=true (legacy behavior)
226-
source := NewMultiSource([]Source{src}, defaultTargets, true)
227+
source := NewMultiSource([]source.Source{src}, defaultTargets, true)
227228

228229
endpoints, err := source.Endpoints(context.Background())
229230
require.NoError(t, err)
@@ -258,7 +259,7 @@ func testMultiSourceEndpointsDefaultTargets(t *testing.T) {
258259
src.On("Endpoints").Return(sourceEndpoints, nil)
259260

260261
// Test with forceDefaultTargets=true
261-
source := NewMultiSource([]Source{src}, defaultTargets, true)
262+
source := NewMultiSource([]source.Source{src}, defaultTargets, true)
262263

263264
endpoints, err := source.Endpoints(context.Background())
264265
require.NoError(t, err)

source/nat64source.go renamed to source/wrappers/nat64source.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,25 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
2121
"fmt"
2222
"net/netip"
2323

2424
"sigs.k8s.io/external-dns/endpoint"
25+
"sigs.k8s.io/external-dns/source"
2526
)
2627

2728
// nat64Source is a Source that adds A endpoints for AAAA records including an NAT64 address.
2829
type nat64Source struct {
29-
source Source
30+
source source.Source
3031
nat64Prefixes []string
3132
}
3233

3334
// NewNAT64Source creates a new nat64Source wrapping the provided Source.
34-
func NewNAT64Source(source Source, nat64Prefixes []string) Source {
35+
func NewNAT64Source(source source.Source, nat64Prefixes []string) source.Source {
3536
return &nat64Source{source: source, nat64Prefixes: nat64Prefixes}
3637
}
3738

source/nat64source_test.go renamed to source/wrappers/nat64source_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package source
17+
package wrappers
1818

1919
import (
2020
"context"
2121
"testing"
2222

2323
"sigs.k8s.io/external-dns/endpoint"
2424
"sigs.k8s.io/external-dns/internal/testutils"
25+
"sigs.k8s.io/external-dns/source"
2526
)
2627

2728
// Validates that dedupSource is a Source
28-
var _ Source = &nat64Source{}
29+
var _ source.Source = &nat64Source{}
2930

3031
func TestNAT64Source(t *testing.T) {
3132
t.Run("Endpoints", testNat64Source)

source/wrappers/source_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package wrappers
18+
19+
import (
20+
"reflect"
21+
"sort"
22+
"testing"
23+
24+
"sigs.k8s.io/external-dns/endpoint"
25+
)
26+
27+
func sortEndpoints(endpoints []*endpoint.Endpoint) {
28+
for _, ep := range endpoints {
29+
sort.Strings([]string(ep.Targets))
30+
}
31+
sort.Slice(endpoints, func(i, k int) bool {
32+
// Sort by DNSName, RecordType, and Targets
33+
ei, ek := endpoints[i], endpoints[k]
34+
if ei.DNSName != ek.DNSName {
35+
return ei.DNSName < ek.DNSName
36+
}
37+
if ei.RecordType != ek.RecordType {
38+
return ei.RecordType < ek.RecordType
39+
}
40+
// Targets are sorted ahead of time.
41+
for j, ti := range ei.Targets {
42+
if j >= len(ek.Targets) {
43+
return true
44+
}
45+
if tk := ek.Targets[j]; ti != tk {
46+
return ti < tk
47+
}
48+
}
49+
return false
50+
})
51+
}
52+
53+
func validateEndpoints(t *testing.T, endpoints, expected []*endpoint.Endpoint) {
54+
t.Helper()
55+
56+
if len(endpoints) != len(expected) {
57+
t.Fatalf("expected %d endpoints, got %d", len(expected), len(endpoints))
58+
}
59+
60+
// Make sure endpoints are sorted - validateEndpoint() depends on it.
61+
sortEndpoints(endpoints)
62+
sortEndpoints(expected)
63+
64+
for i := range endpoints {
65+
validateEndpoint(t, endpoints[i], expected[i])
66+
}
67+
}
68+
69+
func validateEndpoint(t *testing.T, endpoint, expected *endpoint.Endpoint) {
70+
t.Helper()
71+
72+
if endpoint.DNSName != expected.DNSName {
73+
t.Errorf("DNSName expected %q, got %q", expected.DNSName, endpoint.DNSName)
74+
}
75+
76+
if !endpoint.Targets.Same(expected.Targets) {
77+
t.Errorf("Targets expected %q, got %q", expected.Targets, endpoint.Targets)
78+
}
79+
80+
if endpoint.RecordTTL != expected.RecordTTL {
81+
t.Errorf("RecordTTL expected %v, got %v", expected.RecordTTL, endpoint.RecordTTL)
82+
}
83+
84+
// if a non-empty record type is expected, check that it matches.
85+
if endpoint.RecordType != expected.RecordType {
86+
t.Errorf("RecordType expected %q, got %q", expected.RecordType, endpoint.RecordType)
87+
}
88+
89+
// if non-empty labels are expected, check that they match.
90+
if expected.Labels != nil && !reflect.DeepEqual(endpoint.Labels, expected.Labels) {
91+
t.Errorf("Labels expected %s, got %s", expected.Labels, endpoint.Labels)
92+
}
93+
94+
if (len(expected.ProviderSpecific) != 0 || len(endpoint.ProviderSpecific) != 0) &&
95+
!reflect.DeepEqual(endpoint.ProviderSpecific, expected.ProviderSpecific) {
96+
t.Errorf("ProviderSpecific expected %s, got %s", expected.ProviderSpecific, endpoint.ProviderSpecific)
97+
}
98+
99+
if endpoint.SetIdentifier != expected.SetIdentifier {
100+
t.Errorf("SetIdentifier expected %q, got %q", expected.SetIdentifier, endpoint.SetIdentifier)
101+
}
102+
}

0 commit comments

Comments
 (0)