mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
fix(source/service): make sure only unique targets pushed to registry (#5614)
Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
This commit is contained in:
parent
252a5e016c
commit
9045e45bc3
@ -19,6 +19,7 @@ package endpoint
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -353,7 +354,21 @@ func (e *Endpoint) String() string {
|
|||||||
return fmt.Sprintf("%s %d IN %s %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.SetIdentifier, e.Targets, e.ProviderSpecific)
|
return fmt.Sprintf("%s %d IN %s %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.SetIdentifier, e.Targets, e.ProviderSpecific)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply filter to slice of endpoints and return new filtered slice that includes
|
// UniqueOrderedTargets removes duplicate targets from the Endpoint and sorts them in lexicographical order.
|
||||||
|
func (e *Endpoint) UniqueOrderedTargets() {
|
||||||
|
result := make([]string, 0, len(e.Targets))
|
||||||
|
existing := make(map[string]bool)
|
||||||
|
for _, target := range e.Targets {
|
||||||
|
if _, ok := existing[target]; !ok {
|
||||||
|
result = append(result, target)
|
||||||
|
existing[target] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.Sort(result)
|
||||||
|
e.Targets = result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterEndpointsByOwnerID Apply filter to slice of endpoints and return new filtered slice that includes
|
||||||
// only endpoints that match.
|
// only endpoints that match.
|
||||||
func FilterEndpointsByOwnerID(ownerID string, eps []*Endpoint) []*Endpoint {
|
func FilterEndpointsByOwnerID(ownerID string, eps []*Endpoint) []*Endpoint {
|
||||||
filtered := []*Endpoint{}
|
filtered := []*Endpoint{}
|
||||||
|
@ -925,3 +925,46 @@ func TestCheckEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEndpoint_UniqueOrderedTargets(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
targets []string
|
||||||
|
expected Targets
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no duplicates",
|
||||||
|
targets: []string{"b.example.com", "a.example.com"},
|
||||||
|
expected: Targets{"a.example.com", "b.example.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with duplicates",
|
||||||
|
targets: []string{"a.example.com", "b.example.com", "a.example.com"},
|
||||||
|
expected: Targets{"a.example.com", "b.example.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "already sorted",
|
||||||
|
targets: []string{"a.example.com", "b.example.com"},
|
||||||
|
expected: Targets{"a.example.com", "b.example.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all duplicates",
|
||||||
|
targets: []string{"a.example.com", "a.example.com", "a.example.com"},
|
||||||
|
expected: Targets{"a.example.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
targets: []string{},
|
||||||
|
expected: Targets{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ep := &Endpoint{Targets: tt.targets}
|
||||||
|
ep.UniqueOrderedTargets()
|
||||||
|
assert.Equal(t, tt.expected, ep.Targets)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -251,6 +251,29 @@ func (sc *serviceSource) Endpoints(_ context.Context) ([]*endpoint.Endpoint, err
|
|||||||
sort.Slice(endpoints, func(i, j int) bool {
|
sort.Slice(endpoints, func(i, j int) bool {
|
||||||
return endpoints[i].Labels[endpoint.ResourceLabelKey] < endpoints[j].Labels[endpoint.ResourceLabelKey]
|
return endpoints[i].Labels[endpoint.ResourceLabelKey] < endpoints[j].Labels[endpoint.ResourceLabelKey]
|
||||||
})
|
})
|
||||||
|
mergedEndpoints := make(map[endpoint.EndpointKey][]*endpoint.Endpoint)
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
key := ep.Key()
|
||||||
|
if existing, ok := mergedEndpoints[key]; ok {
|
||||||
|
if existing[0].RecordType == endpoint.RecordTypeCNAME {
|
||||||
|
log.Debugf("CNAME %s with multiple targets found", ep.DNSName)
|
||||||
|
mergedEndpoints[key] = append(existing, ep)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existing[0].Targets = append(existing[0].Targets, ep.Targets...)
|
||||||
|
existing[0].UniqueOrderedTargets()
|
||||||
|
mergedEndpoints[key] = existing
|
||||||
|
} else {
|
||||||
|
ep.UniqueOrderedTargets()
|
||||||
|
mergedEndpoints[key] = []*endpoint.Endpoint{ep}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed := make([]*endpoint.Endpoint, 0, len(mergedEndpoints))
|
||||||
|
for _, ep := range mergedEndpoints {
|
||||||
|
processed = append(processed, ep...)
|
||||||
|
}
|
||||||
|
endpoints = processed
|
||||||
|
|
||||||
// Use stable sort to not disrupt the order of services
|
// Use stable sort to not disrupt the order of services
|
||||||
sort.SliceStable(endpoints, func(i, j int) bool {
|
sort.SliceStable(endpoints, func(i, j int) bool {
|
||||||
if endpoints[i].DNSName != endpoints[j].DNSName {
|
if endpoints[i].DNSName != endpoints[j].DNSName {
|
||||||
@ -258,31 +281,6 @@ func (sc *serviceSource) Endpoints(_ context.Context) ([]*endpoint.Endpoint, err
|
|||||||
}
|
}
|
||||||
return endpoints[i].RecordType < endpoints[j].RecordType
|
return endpoints[i].RecordType < endpoints[j].RecordType
|
||||||
})
|
})
|
||||||
mergedEndpoints := []*endpoint.Endpoint{}
|
|
||||||
mergedEndpoints = append(mergedEndpoints, endpoints[0])
|
|
||||||
for i := 1; i < len(endpoints); i++ {
|
|
||||||
lastMergedEndpoint := len(mergedEndpoints) - 1
|
|
||||||
if mergedEndpoints[lastMergedEndpoint].DNSName == endpoints[i].DNSName &&
|
|
||||||
mergedEndpoints[lastMergedEndpoint].RecordType == endpoints[i].RecordType &&
|
|
||||||
mergedEndpoints[lastMergedEndpoint].RecordType != endpoint.RecordTypeCNAME && // It is against RFC-1034 for CNAME records to have multiple targets, so skip merging
|
|
||||||
mergedEndpoints[lastMergedEndpoint].SetIdentifier == endpoints[i].SetIdentifier &&
|
|
||||||
mergedEndpoints[lastMergedEndpoint].RecordTTL == endpoints[i].RecordTTL {
|
|
||||||
mergedEndpoints[lastMergedEndpoint].Targets = append(mergedEndpoints[lastMergedEndpoint].Targets, endpoints[i].Targets[0])
|
|
||||||
} else {
|
|
||||||
mergedEndpoints = append(mergedEndpoints, endpoints[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
if mergedEndpoints[lastMergedEndpoint].DNSName == endpoints[i].DNSName &&
|
|
||||||
mergedEndpoints[lastMergedEndpoint].RecordType == endpoints[i].RecordType &&
|
|
||||||
mergedEndpoints[lastMergedEndpoint].RecordType == endpoint.RecordTypeCNAME {
|
|
||||||
log.Debugf("CNAME %s with multiple targets found", endpoints[i].DNSName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endpoints = mergedEndpoints
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ep := range endpoints {
|
|
||||||
sort.Sort(ep.Targets)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
|
@ -30,10 +30,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
discoveryv1 "k8s.io/api/discovery/v1"
|
discoveryv1 "k8s.io/api/discovery/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
@ -3089,7 +3091,7 @@ func TestHeadlessServices(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create a Kubernetes testing client
|
// Create a Kubernetes testing client
|
||||||
kubernetes := fake.NewSimpleClientset()
|
kubernetes := fake.NewClientset()
|
||||||
|
|
||||||
service := &v1.Service{
|
service := &v1.Service{
|
||||||
Spec: v1.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
@ -3196,6 +3198,378 @@ func TestHeadlessServices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultipleHeadlessServicesPointingToPodsOnTheSameNode(t *testing.T) {
|
||||||
|
kubernetes := fake.NewClientset()
|
||||||
|
|
||||||
|
headless := []*v1.Service{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
annotations.HostnameKey: "example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.ServiceSpec{
|
||||||
|
Type: v1.ServiceTypeClusterIP,
|
||||||
|
ClusterIP: v1.ClusterIPNone,
|
||||||
|
ClusterIPs: []string{v1.ClusterIPNone},
|
||||||
|
InternalTrafficPolicy: testutils.ToPtr(v1.ServiceInternalTrafficPolicyCluster),
|
||||||
|
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
|
||||||
|
IPFamilyPolicy: testutils.ToPtr(v1.IPFamilyPolicySingleStack),
|
||||||
|
Ports: []v1.ServicePort{
|
||||||
|
{
|
||||||
|
Name: "web",
|
||||||
|
Port: 80,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
TargetPort: intstr.FromInt32(80),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selector: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
},
|
||||||
|
SessionAffinity: v1.ServiceAffinityNone,
|
||||||
|
},
|
||||||
|
Status: v1.ServiceStatus{
|
||||||
|
LoadBalancer: v1.LoadBalancerStatus{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-2",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
annotations.HostnameKey: "example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.ServiceSpec{
|
||||||
|
Type: v1.ServiceTypeClusterIP,
|
||||||
|
ClusterIP: v1.ClusterIPNone,
|
||||||
|
ClusterIPs: []string{v1.ClusterIPNone},
|
||||||
|
InternalTrafficPolicy: testutils.ToPtr(v1.ServiceInternalTrafficPolicyCluster),
|
||||||
|
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
|
||||||
|
IPFamilyPolicy: testutils.ToPtr(v1.IPFamilyPolicySingleStack),
|
||||||
|
Ports: []v1.ServicePort{
|
||||||
|
{
|
||||||
|
Name: "web",
|
||||||
|
Port: 80,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
TargetPort: intstr.FromInt32(80),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selector: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
},
|
||||||
|
SessionAffinity: v1.ServiceAffinityNone,
|
||||||
|
},
|
||||||
|
Status: v1.ServiceStatus{
|
||||||
|
LoadBalancer: v1.LoadBalancerStatus{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotNil(t, headless)
|
||||||
|
|
||||||
|
pods := []*v1.Pod{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-0",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
appsv1.PodIndexLabel: "0",
|
||||||
|
appsv1.ControllerRevisionHashLabelKey: "kafka-b8d79cdb6",
|
||||||
|
appsv1.StatefulSetPodNameLabel: "kafka-0",
|
||||||
|
},
|
||||||
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
|
{
|
||||||
|
APIVersion: "apps/v1",
|
||||||
|
Kind: "StatefulSet",
|
||||||
|
Name: "kafka",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Hostname: "kafka-0",
|
||||||
|
Subdomain: "kafka",
|
||||||
|
NodeName: "local-dev-worker",
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Ports: []v1.ContainerPort{
|
||||||
|
{Name: "web", ContainerPort: 80, Protocol: v1.ProtocolTCP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1.PodStatus{
|
||||||
|
Phase: v1.PodRunning,
|
||||||
|
PodIP: "10.244.1.2",
|
||||||
|
PodIPs: []v1.PodIP{{IP: "10.244.1.2"}},
|
||||||
|
HostIP: "172.18.0.2",
|
||||||
|
HostIPs: []v1.HostIP{{IP: "172.18.0.2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-1",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
appsv1.PodIndexLabel: "1",
|
||||||
|
appsv1.ControllerRevisionHashLabelKey: "kafka-b8d79cdb6",
|
||||||
|
appsv1.StatefulSetPodNameLabel: "kafka-1",
|
||||||
|
},
|
||||||
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
|
{
|
||||||
|
APIVersion: "apps/v1",
|
||||||
|
Kind: "StatefulSet",
|
||||||
|
Name: "kafka",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Hostname: "kafka-1",
|
||||||
|
Subdomain: "kafka",
|
||||||
|
NodeName: "local-dev-worker",
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Ports: []v1.ContainerPort{
|
||||||
|
{Name: "web", ContainerPort: 80, Protocol: v1.ProtocolTCP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1.PodStatus{
|
||||||
|
Phase: v1.PodRunning,
|
||||||
|
PodIP: "10.244.1.3",
|
||||||
|
PodIPs: []v1.PodIP{{IP: "10.244.1.3"}},
|
||||||
|
HostIP: "172.18.0.2",
|
||||||
|
HostIPs: []v1.HostIP{{IP: "172.18.0.2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-2",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
appsv1.PodIndexLabel: "2",
|
||||||
|
appsv1.ControllerRevisionHashLabelKey: "kafka-b8d79cdb6",
|
||||||
|
appsv1.StatefulSetPodNameLabel: "kafka-2",
|
||||||
|
},
|
||||||
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
|
{
|
||||||
|
APIVersion: "apps/v1",
|
||||||
|
Kind: "StatefulSet",
|
||||||
|
Name: "kafka",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Hostname: "kafka-2",
|
||||||
|
Subdomain: "kafka",
|
||||||
|
NodeName: "local-dev-worker",
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Ports: []v1.ContainerPort{
|
||||||
|
{Name: "web", ContainerPort: 80, Protocol: v1.ProtocolTCP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: v1.PodStatus{
|
||||||
|
Phase: v1.PodRunning,
|
||||||
|
PodIP: "10.244.1.4",
|
||||||
|
PodIPs: []v1.PodIP{{IP: "10.244.1.4"}},
|
||||||
|
HostIP: "172.18.0.2",
|
||||||
|
HostIPs: []v1.HostIP{{IP: "172.18.0.2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Len(t, pods, 3)
|
||||||
|
|
||||||
|
endpoints := []*discoveryv1.EndpointSlice{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-xhrc9",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
discoveryv1.LabelServiceName: "kafka",
|
||||||
|
discoveryv1.LabelManagedBy: "endpointslice-controller.k8s.io",
|
||||||
|
v1.IsHeadlessService: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AddressType: discoveryv1.AddressTypeIPv4,
|
||||||
|
Endpoints: []discoveryv1.Endpoint{
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.2"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-0"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-0",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.3"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-1"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.4"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-2"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-2",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "kafka-2-svwsg",
|
||||||
|
Namespace: "default",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "kafka",
|
||||||
|
discoveryv1.LabelServiceName: "kafka-2",
|
||||||
|
discoveryv1.LabelManagedBy: "endpointslice-controller.k8s.io",
|
||||||
|
v1.IsHeadlessService: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AddressType: discoveryv1.AddressTypeIPv4,
|
||||||
|
Endpoints: []discoveryv1.Endpoint{
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.2"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-0"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-0",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.3"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-1"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addresses: []string{"10.244.1.4"},
|
||||||
|
Hostname: testutils.ToPtr("kafka-2"),
|
||||||
|
NodeName: testutils.ToPtr("local-dev-worker"),
|
||||||
|
TargetRef: &v1.ObjectReference{
|
||||||
|
Kind: "Pod",
|
||||||
|
Name: "kafka-2",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Conditions: discoveryv1.EndpointConditions{
|
||||||
|
Ready: testutils.ToPtr(true),
|
||||||
|
Serving: testutils.ToPtr(true),
|
||||||
|
Terminating: testutils.ToPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, svc := range headless {
|
||||||
|
_, err := kubernetes.CoreV1().Services(svc.Namespace).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pod := range pods {
|
||||||
|
_, err := kubernetes.CoreV1().Pods(pod.Namespace).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
_, err := kubernetes.DiscoveryV1().EndpointSlices(ep.Namespace).Create(context.Background(), ep, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := NewServiceSource(
|
||||||
|
t.Context(),
|
||||||
|
kubernetes,
|
||||||
|
v1.NamespaceAll,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[]string{},
|
||||||
|
false,
|
||||||
|
labels.Everything(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, src)
|
||||||
|
|
||||||
|
got, err := src.Endpoints(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
want := []*endpoint.Endpoint{
|
||||||
|
// TODO: root domain records should not be created. Address them in a follow-up PR.
|
||||||
|
{DNSName: "example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.244.1.2", "10.244.1.3", "10.244.1.4"}},
|
||||||
|
{DNSName: "kafka-0.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.244.1.2"}},
|
||||||
|
{DNSName: "kafka-1.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.244.1.3"}},
|
||||||
|
{DNSName: "kafka-2.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.244.1.4"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
validateEndpoints(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
// TestHeadlessServices tests that headless services generate the correct endpoints.
|
// TestHeadlessServices tests that headless services generate the correct endpoints.
|
||||||
func TestHeadlessServicesHostIP(t *testing.T) {
|
func TestHeadlessServicesHostIP(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
Loading…
Reference in New Issue
Block a user