external-dns/source/endpoints_test.go
Ivan Ka aa2a488b53
refactore(source): move EndpointsForHostname to endpoints package (#6248)
* refactore(source): move EndpointsForHostname to endpoint package

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* refactore(source): move EndpointsForHostname to endpoint package

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* refactore(source): move EndpointsForHostname to endpoint package

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

---------

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
2026-03-12 12:57:36 +05:30

250 lines
6.4 KiB
Go

/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package source
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"sigs.k8s.io/external-dns/endpoint"
)
func TestEndpointTargetsFromServices(t *testing.T) {
tests := []struct {
name string
services []*corev1.Service
namespace string
selector map[string]string
expected endpoint.Targets
wantErr bool
}{
{
name: "no services",
services: []*corev1.Service{},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{},
},
{
name: "matching service with external IPs",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "nginx"},
ExternalIPs: []string{"192.0.2.1", "158.123.32.23"},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{"158.123.32.23", "192.0.2.1"},
},
{
name: "matching service with duplicate external IPs",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "nginx"},
ExternalIPs: []string{"192.0.2.1", "192.0.2.1", "158.123.32.23"},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{"158.123.32.23", "192.0.2.1"},
},
{
name: "no matching service as service without selector",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
ExternalIPs: []string{"192.0.2.1"},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{},
},
{
name: "matching service with load balancer IP",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc2",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "nginx"},
},
Status: corev1.ServiceStatus{
LoadBalancer: corev1.LoadBalancerStatus{
Ingress: []corev1.LoadBalancerIngress{
{IP: "192.0.2.2"},
},
},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{"192.0.2.2"},
},
{
name: "matching service with load balancer hostname",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc3",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "nginx"},
},
Status: corev1.ServiceStatus{
LoadBalancer: corev1.LoadBalancerStatus{
Ingress: []corev1.LoadBalancerIngress{
{Hostname: "lb.example.com"},
},
},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{"lb.example.com"},
},
{
name: "no matching services",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc4",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "apache"},
},
},
},
namespace: "default",
selector: map[string]string{"app": "nginx"},
expected: endpoint.Targets{},
},
{
name: "multiple selectors",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "fake",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{"app": "apache", "version": "v1"},
ExternalIPs: []string{"158.123.32.23"},
},
},
},
namespace: "default",
selector: map[string]string{"version": "v1"},
expected: endpoint.Targets{"158.123.32.23"},
},
{
name: "complex selectors",
services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "fake",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "demo",
"env": "prod",
"team": "devops",
"version": "v1",
"release": "stable",
"track": "daily",
"tier": "backend",
},
ExternalIPs: []string{"158.123.32.23"},
},
},
},
namespace: "default",
selector: map[string]string{
"version": "v1",
"release": "stable",
"tier": "backend",
"app": "demo",
},
expected: endpoint.Targets{"158.123.32.23"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := fake.NewClientset()
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(client, 0,
kubeinformers.WithNamespace(tt.namespace))
serviceInformer := informerFactory.Core().V1().Services()
for _, svc := range tt.services {
_, err := client.CoreV1().Services(tt.namespace).Create(context.Background(), svc, metav1.CreateOptions{})
assert.NoError(t, err)
err = serviceInformer.Informer().GetIndexer().Add(svc)
assert.NoError(t, err)
}
result, err := EndpointTargetsFromServices(serviceInformer, tt.namespace, tt.selector)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestEndpointTargetsFromServicesWithFixtures(t *testing.T) {
svcInformer, err := svcInformerWithServices(2, 9)
assert.NoError(t, err)
sel := map[string]string{"app": "nginx", "env": "prod"}
targets, err := EndpointTargetsFromServices(svcInformer, "default", sel)
assert.NoError(t, err)
assert.Equal(t, 2, targets.Len())
}