mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 09:06:58 +02:00
fix(source/service): disable pod and endpointSlices informers when they are not needed (#5646)
* fix(source/service): disable pod and endpointSlicesInformer when not required Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * fix(source/service): disable pod and endpointSlicesInformer when not required Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * fix(source/service): disable pod and endpointSlices informers when they are not needed Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * fix(source/service): disable pod and endpointSlices informers when they are not needed Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> --------- Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
This commit is contained in:
parent
0d1309c7fa
commit
1b9d7cddc0
60
source/informers/fake.go
Normal file
60
source/informers/fake.go
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 informers
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
corev1lister "k8s.io/client-go/listers/core/v1"
|
||||
discoveryv1lister "k8s.io/client-go/listers/discovery/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
type FakeServiceInformer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *FakeServiceInformer) Informer() cache.SharedIndexInformer {
|
||||
args := f.Called()
|
||||
return args.Get(0).(cache.SharedIndexInformer)
|
||||
}
|
||||
|
||||
func (f *FakeServiceInformer) Lister() corev1lister.ServiceLister {
|
||||
return corev1lister.NewServiceLister(f.Informer().GetIndexer())
|
||||
}
|
||||
|
||||
type FakeEndpointSliceInformer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *FakeEndpointSliceInformer) Informer() cache.SharedIndexInformer {
|
||||
args := f.Called()
|
||||
return args.Get(0).(cache.SharedIndexInformer)
|
||||
}
|
||||
|
||||
func (f *FakeEndpointSliceInformer) Lister() discoveryv1lister.EndpointSliceLister {
|
||||
return discoveryv1lister.NewEndpointSliceLister(f.Informer().GetIndexer())
|
||||
}
|
||||
|
||||
type FakeNodeInformer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *FakeNodeInformer) Informer() cache.SharedIndexInformer {
|
||||
args := f.Called()
|
||||
return args.Get(0).(cache.SharedIndexInformer)
|
||||
}
|
||||
|
||||
func (f *FakeNodeInformer) Lister() corev1lister.NodeLister {
|
||||
return corev1lister.NewNodeLister(f.Informer().GetIndexer())
|
||||
}
|
@ -96,28 +96,9 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name
|
||||
// Set the resync period to 0 to prevent processing when nothing has changed
|
||||
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
|
||||
serviceInformer := informerFactory.Core().V1().Services()
|
||||
endpointSlicesInformer := informerFactory.Discovery().V1().EndpointSlices()
|
||||
podInformer := informerFactory.Core().V1().Pods()
|
||||
|
||||
// Add default resource event handlers to properly initialize informer.
|
||||
_, _ = serviceInformer.Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
},
|
||||
},
|
||||
)
|
||||
_, _ = endpointSlicesInformer.Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
},
|
||||
},
|
||||
)
|
||||
_, _ = podInformer.Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
},
|
||||
},
|
||||
)
|
||||
_, _ = serviceInformer.Informer().AddEventHandler(informers.DefaultEventHandler())
|
||||
|
||||
// Transform the slice into a map so it will be way much easier and fast to filter later
|
||||
sTypesFilter, err := newServiceTypesFilter(serviceTypeFilter)
|
||||
@ -125,30 +106,40 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodeInformer coreinformers.NodeInformer
|
||||
if sTypesFilter.isNodeInformerRequired() {
|
||||
nodeInformer = informerFactory.Core().V1().Nodes()
|
||||
_, _ = nodeInformer.Informer().AddEventHandler(informers.DefaultEventHandler())
|
||||
var endpointSlicesInformer discoveryinformers.EndpointSliceInformer
|
||||
var podInformer coreinformers.PodInformer
|
||||
if sTypesFilter.isRequired(v1.ServiceTypeNodePort, v1.ServiceTypeClusterIP) {
|
||||
endpointSlicesInformer = informerFactory.Discovery().V1().EndpointSlices()
|
||||
podInformer = informerFactory.Core().V1().Pods()
|
||||
|
||||
_, _ = endpointSlicesInformer.Informer().AddEventHandler(informers.DefaultEventHandler())
|
||||
_, _ = podInformer.Informer().AddEventHandler(informers.DefaultEventHandler())
|
||||
|
||||
// Add an indexer to the EndpointSlice informer to index by the service name label
|
||||
err = endpointSlicesInformer.Informer().AddIndexers(cache.Indexers{
|
||||
serviceNameIndexKey: func(obj any) ([]string, error) {
|
||||
endpointSlice, ok := obj.(*discoveryv1.EndpointSlice)
|
||||
if !ok {
|
||||
// This should never happen because the Informer should only contain EndpointSlice objects
|
||||
return nil, fmt.Errorf("expected %T but got %T instead", endpointSlice, obj)
|
||||
}
|
||||
serviceName := endpointSlice.Labels[discoveryv1.LabelServiceName]
|
||||
if serviceName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
key := types.NamespacedName{Namespace: endpointSlice.Namespace, Name: serviceName}.String()
|
||||
return []string{key}, nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add an indexer to the EndpointSlice informer to index by the service name label
|
||||
err = endpointSlicesInformer.Informer().AddIndexers(cache.Indexers{
|
||||
serviceNameIndexKey: func(obj any) ([]string, error) {
|
||||
endpointSlice, ok := obj.(*discoveryv1.EndpointSlice)
|
||||
if !ok {
|
||||
// This should never happen because the Informer should only contain EndpointSlice objects
|
||||
return nil, fmt.Errorf("expected %T but got %T instead", endpointSlice, obj)
|
||||
}
|
||||
serviceName := endpointSlice.Labels[discoveryv1.LabelServiceName]
|
||||
if serviceName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
key := types.NamespacedName{Namespace: endpointSlice.Namespace, Name: serviceName}.String()
|
||||
return []string{key}, nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var nodeInformer coreinformers.NodeInformer
|
||||
if sTypesFilter.isRequired(v1.ServiceTypeNodePort) {
|
||||
nodeInformer = informerFactory.Core().V1().Nodes()
|
||||
_, _ = nodeInformer.Informer().AddEventHandler(informers.DefaultEventHandler())
|
||||
}
|
||||
|
||||
informerFactory.Start(ctx.Done())
|
||||
@ -808,10 +799,10 @@ func (sc *serviceSource) AddEventHandler(_ context.Context, handler func()) {
|
||||
// Right now there is no way to remove event handler from informer, see:
|
||||
// https://github.com/kubernetes/kubernetes/issues/79610
|
||||
_, _ = sc.serviceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
if sc.listenEndpointEvents {
|
||||
if sc.listenEndpointEvents && sc.serviceTypeFilter.isRequired(v1.ServiceTypeNodePort, v1.ServiceTypeClusterIP) {
|
||||
_, _ = sc.endpointSlicesInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
}
|
||||
if sc.serviceTypeFilter.isNodeInformerRequired() {
|
||||
if sc.serviceTypeFilter.isRequired(v1.ServiceTypeNodePort) {
|
||||
_, _ = sc.nodeInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
}
|
||||
}
|
||||
@ -848,12 +839,18 @@ func (sc *serviceTypes) isProcessed(serviceType v1.ServiceType) bool {
|
||||
return !sc.enabled || sc.types[serviceType]
|
||||
}
|
||||
|
||||
func (sc *serviceTypes) isNodeInformerRequired() bool {
|
||||
if !sc.enabled {
|
||||
// isRequired returns true if service type filtering is disabled or if any of the provided service types are present in the filter.
|
||||
// If no options are provided, it returns true.
|
||||
func (sc *serviceTypes) isRequired(opts ...v1.ServiceType) bool {
|
||||
if len(opts) == 0 || !sc.enabled {
|
||||
return true
|
||||
}
|
||||
_, ok := sc.types[v1.ServiceTypeNodePort]
|
||||
return ok
|
||||
for _, opt := range opts {
|
||||
if _, ok := sc.types[opt]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// conditionToBool converts an EndpointConditions condition to a bool value.
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"sigs.k8s.io/external-dns/source/informers"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
@ -251,7 +252,7 @@ func testServiceSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
externalIPs: []string{},
|
||||
lbs: []string{"1.2.3.4"},
|
||||
serviceTypesFilter: []string{},
|
||||
serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
@ -296,7 +297,7 @@ func testServiceSourceEndpoints(t *testing.T) {
|
||||
annotations: map[string]string{},
|
||||
externalIPs: []string{},
|
||||
lbs: []string{"1.2.3.4"},
|
||||
serviceTypesFilter: []string{},
|
||||
serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer), string(v1.ServiceTypeNodePort)},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
{DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
@ -2498,6 +2499,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
podsReady []bool
|
||||
publishNotReadyAddresses bool
|
||||
nodes []v1.Node
|
||||
serviceTypesFilter []string
|
||||
expected []*endpoint.Endpoint
|
||||
expectError bool
|
||||
}{
|
||||
@ -2528,6 +2530,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
|
||||
@ -2562,6 +2565,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{string(v1.ServiceTypeClusterIP), string(v1.ServiceTypeLoadBalancer)},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
|
||||
@ -2596,6 +2600,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{},
|
||||
false,
|
||||
},
|
||||
@ -2627,6 +2632,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)},
|
||||
@ -2662,6 +2668,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
|
||||
@ -2696,6 +2703,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, false},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
|
||||
@ -2729,6 +2737,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, false},
|
||||
true,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
|
||||
@ -2763,6 +2772,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
|
||||
},
|
||||
@ -2795,6 +2805,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
|
||||
},
|
||||
@ -2827,6 +2838,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
|
||||
},
|
||||
@ -2861,6 +2873,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{string(v1.ServiceTypeClusterIP)},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
@ -2895,6 +2908,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
@ -2939,6 +2953,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
@ -2987,6 +3002,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::5"}},
|
||||
},
|
||||
@ -3031,6 +3047,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
@ -3079,6 +3096,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
@ -3113,6 +3131,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
@ -3146,6 +3165,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
@ -3242,7 +3262,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
[]string{},
|
||||
tc.serviceTypesFilter,
|
||||
tc.ignoreHostnameAnnotation,
|
||||
labels.Everything(),
|
||||
false,
|
||||
@ -3994,7 +4014,6 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a Kubernetes testing client
|
||||
kubernetes := fake.NewClientset()
|
||||
|
||||
service := &v1.Service{
|
||||
@ -4134,7 +4153,7 @@ func TestExternalServices(t *testing.T) {
|
||||
},
|
||||
"111.111.111.111",
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{string(v1.ServiceTypeNodePort), string(v1.ServiceTypeExternalName)},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", Targets: endpoint.Targets{"111.111.111.111"}, RecordType: endpoint.RecordTypeA},
|
||||
},
|
||||
@ -4176,7 +4195,7 @@ func TestExternalServices(t *testing.T) {
|
||||
},
|
||||
"remote.example.com",
|
||||
[]string{},
|
||||
[]string{},
|
||||
[]string{string(v1.ServiceTypeExternalName)},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", Targets: endpoint.Targets{"remote.example.com"}, RecordType: endpoint.RecordTypeCNAME},
|
||||
},
|
||||
@ -4371,6 +4390,8 @@ func TestNewServiceSourceInformersEnabled(t *testing.T) {
|
||||
assert.NotNil(t, svc.serviceTypeFilter)
|
||||
assert.False(t, svc.serviceTypeFilter.enabled)
|
||||
assert.NotNil(t, svc.nodeInformer)
|
||||
assert.NotNil(t, svc.serviceInformer)
|
||||
assert.NotNil(t, svc.endpointSlicesInformer)
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -4380,17 +4401,49 @@ func TestNewServiceSourceInformersEnabled(t *testing.T) {
|
||||
assert.NotNil(t, svc)
|
||||
assert.NotNil(t, svc.serviceTypeFilter)
|
||||
assert.True(t, svc.serviceTypeFilter.enabled)
|
||||
assert.NotNil(t, svc.serviceInformer)
|
||||
assert.Nil(t, svc.nodeInformer)
|
||||
assert.NotNil(t, svc.endpointSlicesInformer)
|
||||
assert.NotNil(t, svc.podInformer)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "serviceTypeFilter contains NodePort",
|
||||
svcFilter: []string{string(v1.ServiceTypeNodePort)},
|
||||
name: "serviceTypeFilter contains NodePort and ExternalName",
|
||||
svcFilter: []string{string(v1.ServiceTypeNodePort), string(v1.ServiceTypeExternalName)},
|
||||
asserts: func(svc *serviceSource) {
|
||||
assert.NotNil(t, svc)
|
||||
assert.NotNil(t, svc.serviceTypeFilter)
|
||||
assert.True(t, svc.serviceTypeFilter.enabled)
|
||||
assert.NotNil(t, svc.serviceInformer)
|
||||
assert.NotNil(t, svc.nodeInformer)
|
||||
assert.NotNil(t, svc.endpointSlicesInformer)
|
||||
assert.NotNil(t, svc.podInformer)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "serviceTypeFilter contains ExternalName",
|
||||
svcFilter: []string{string(v1.ServiceTypeExternalName)},
|
||||
asserts: func(svc *serviceSource) {
|
||||
assert.NotNil(t, svc)
|
||||
assert.NotNil(t, svc.serviceTypeFilter)
|
||||
assert.True(t, svc.serviceTypeFilter.enabled)
|
||||
assert.NotNil(t, svc.serviceInformer)
|
||||
assert.Nil(t, svc.nodeInformer)
|
||||
assert.Nil(t, svc.endpointSlicesInformer)
|
||||
assert.Nil(t, svc.podInformer)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "serviceTypeFilter contains LoadBalancer",
|
||||
svcFilter: []string{string(v1.ServiceTypeLoadBalancer)},
|
||||
asserts: func(svc *serviceSource) {
|
||||
assert.NotNil(t, svc)
|
||||
assert.NotNil(t, svc.serviceTypeFilter)
|
||||
assert.True(t, svc.serviceTypeFilter.enabled)
|
||||
assert.NotNil(t, svc.serviceInformer)
|
||||
assert.Nil(t, svc.nodeInformer)
|
||||
assert.Nil(t, svc.endpointSlicesInformer)
|
||||
assert.Nil(t, svc.podInformer)
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -4681,32 +4734,126 @@ func createTestServicesByType(namespace string, typeCounts map[v1.ServiceType]in
|
||||
|
||||
func TestServiceTypes_isNodeInformerRequired(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filter []string
|
||||
want bool
|
||||
name string
|
||||
filter []string
|
||||
required []v1.ServiceType
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "NodePort type present",
|
||||
filter: []string{string(v1.ServiceTypeNodePort)},
|
||||
want: true,
|
||||
name: "NodePort required and filter is empty",
|
||||
filter: []string{},
|
||||
required: []v1.ServiceType{v1.ServiceTypeNodePort},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "NodePort type absent, filter enabled",
|
||||
filter: []string{string(v1.ServiceTypeLoadBalancer)},
|
||||
want: false,
|
||||
name: "NodePort type present",
|
||||
filter: []string{string(v1.ServiceTypeNodePort)},
|
||||
required: []v1.ServiceType{v1.ServiceTypeNodePort},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "NodePort and other filters present",
|
||||
filter: []string{string(v1.ServiceTypeLoadBalancer), string(v1.ServiceTypeNodePort)},
|
||||
want: true,
|
||||
name: "NodePort type absent, filter enabled",
|
||||
filter: []string{string(v1.ServiceTypeLoadBalancer)},
|
||||
required: []v1.ServiceType{v1.ServiceTypeNodePort},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "NodePort and other filters present",
|
||||
filter: []string{string(v1.ServiceTypeLoadBalancer), string(v1.ServiceTypeNodePort)},
|
||||
required: []v1.ServiceType{v1.ServiceTypeNodePort},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
filter, _ := newServiceTypesFilter(tt.filter)
|
||||
got := filter.isNodeInformerRequired()
|
||||
got := filter.isRequired(tt.required...)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceSource_AddEventHandler(t *testing.T) {
|
||||
var fakeServiceInformer *informers.FakeServiceInformer
|
||||
var fakeEdpInformer *informers.FakeEndpointSliceInformer
|
||||
var fakeNodeInformer *informers.FakeNodeInformer
|
||||
tests := []struct {
|
||||
name string
|
||||
filter []string
|
||||
times int
|
||||
asserts func(t *testing.T, s *serviceSource)
|
||||
}{
|
||||
{
|
||||
name: "AddEventHandler should trigger all event handlers when empty filter is provided",
|
||||
filter: []string{},
|
||||
times: 3,
|
||||
asserts: func(t *testing.T, s *serviceSource) {
|
||||
fakeServiceInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeEdpInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeNodeInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddEventHandler should trigger only service event handler",
|
||||
filter: []string{string(v1.ServiceTypeExternalName), string(v1.ServiceTypeLoadBalancer)},
|
||||
times: 1,
|
||||
asserts: func(t *testing.T, s *serviceSource) {
|
||||
fakeServiceInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeEdpInformer.AssertNumberOfCalls(t, "Informer", 0)
|
||||
fakeNodeInformer.AssertNumberOfCalls(t, "Informer", 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddEventHandler should configure only service event handler",
|
||||
filter: []string{string(v1.ServiceTypeExternalName), string(v1.ServiceTypeLoadBalancer), string(v1.ServiceTypeClusterIP)},
|
||||
times: 2,
|
||||
asserts: func(t *testing.T, s *serviceSource) {
|
||||
fakeServiceInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeEdpInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeNodeInformer.AssertNumberOfCalls(t, "Informer", 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddEventHandler should configure all service event handlers",
|
||||
filter: []string{string(v1.ServiceTypeNodePort)},
|
||||
times: 3,
|
||||
asserts: func(t *testing.T, s *serviceSource) {
|
||||
fakeServiceInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeEdpInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
fakeNodeInformer.AssertNumberOfCalls(t, "Informer", 1)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fakeServiceInformer = new(informers.FakeServiceInformer)
|
||||
infSvc := testInformer{}
|
||||
fakeServiceInformer.On("Informer").Return(&infSvc)
|
||||
|
||||
fakeEdpInformer = new(informers.FakeEndpointSliceInformer)
|
||||
infEdp := testInformer{}
|
||||
fakeEdpInformer.On("Informer").Return(&infEdp)
|
||||
|
||||
fakeNodeInformer = new(informers.FakeNodeInformer)
|
||||
infNode := testInformer{}
|
||||
fakeNodeInformer.On("Informer").Return(&infNode)
|
||||
|
||||
filter, _ := newServiceTypesFilter(tt.filter)
|
||||
|
||||
svcSource := &serviceSource{
|
||||
endpointSlicesInformer: fakeEdpInformer,
|
||||
serviceInformer: fakeServiceInformer,
|
||||
nodeInformer: fakeNodeInformer,
|
||||
serviceTypeFilter: filter,
|
||||
listenEndpointEvents: true,
|
||||
}
|
||||
|
||||
svcSource.AddEventHandler(t.Context(), func() {})
|
||||
|
||||
assert.Equal(t, tt.times, infSvc.times+infEdp.times+infNode.times)
|
||||
|
||||
tt.asserts(t, svcSource)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user