k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/proxy/config/api_test.go (about)

     1  /*
     2  Copyright 2014 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 config
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	discoveryv1 "k8s.io/api/discovery/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	"k8s.io/apimachinery/pkg/util/wait"
    29  	"k8s.io/apimachinery/pkg/watch"
    30  	"k8s.io/client-go/informers"
    31  	"k8s.io/client-go/kubernetes/fake"
    32  	ktesting "k8s.io/client-go/testing"
    33  	klogtesting "k8s.io/klog/v2/ktesting"
    34  	"k8s.io/utils/ptr"
    35  )
    36  
    37  func TestNewServicesSourceApi_UpdatesAndMultipleServices(t *testing.T) {
    38  	_, ctx := klogtesting.NewTestContext(t)
    39  	service1v1 := &v1.Service{
    40  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s1"},
    41  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Protocol: "TCP", Port: 10}}}}
    42  	service1v2 := &v1.Service{
    43  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s1"},
    44  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Protocol: "TCP", Port: 20}}}}
    45  	service2 := &v1.Service{
    46  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s2"},
    47  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Protocol: "TCP", Port: 30}}}}
    48  
    49  	// Setup fake api client.
    50  	client := fake.NewSimpleClientset()
    51  	fakeWatch := watch.NewFake()
    52  	client.PrependWatchReactor("services", ktesting.DefaultWatchReactor(fakeWatch, nil))
    53  
    54  	stopCh := make(chan struct{})
    55  	defer close(stopCh)
    56  
    57  	handler := NewServiceHandlerMock()
    58  
    59  	sharedInformers := informers.NewSharedInformerFactory(client, time.Minute)
    60  
    61  	serviceConfig := NewServiceConfig(ctx, sharedInformers.Core().V1().Services(), time.Minute)
    62  	serviceConfig.RegisterEventHandler(handler)
    63  	go sharedInformers.Start(stopCh)
    64  	go serviceConfig.Run(stopCh)
    65  
    66  	// Add the first service
    67  	fakeWatch.Add(service1v1)
    68  	handler.ValidateServices(t, []*v1.Service{service1v1})
    69  
    70  	// Add another service
    71  	fakeWatch.Add(service2)
    72  	handler.ValidateServices(t, []*v1.Service{service1v1, service2})
    73  
    74  	// Modify service1
    75  	fakeWatch.Modify(service1v2)
    76  	handler.ValidateServices(t, []*v1.Service{service1v2, service2})
    77  
    78  	// Delete service1
    79  	fakeWatch.Delete(service1v2)
    80  	handler.ValidateServices(t, []*v1.Service{service2})
    81  
    82  	// Delete service2
    83  	fakeWatch.Delete(service2)
    84  	handler.ValidateServices(t, []*v1.Service{})
    85  }
    86  
    87  func TestNewEndpointsSourceApi_UpdatesAndMultipleEndpoints(t *testing.T) {
    88  	_, ctx := klogtesting.NewTestContext(t)
    89  	endpoints1v1 := &discoveryv1.EndpointSlice{
    90  		ObjectMeta:  metav1.ObjectMeta{Namespace: "testnamespace", Name: "e1"},
    91  		AddressType: discoveryv1.AddressTypeIPv4,
    92  		Endpoints: []discoveryv1.Endpoint{{
    93  			Addresses: []string{
    94  				"1.2.3.4",
    95  			},
    96  		}},
    97  		Ports: []discoveryv1.EndpointPort{{
    98  			Port:     ptr.To[int32](8080),
    99  			Protocol: ptr.To(v1.ProtocolTCP),
   100  		}},
   101  	}
   102  	endpoints1v2 := &discoveryv1.EndpointSlice{
   103  		ObjectMeta:  metav1.ObjectMeta{Namespace: "testnamespace", Name: "e1"},
   104  		AddressType: discoveryv1.AddressTypeIPv4,
   105  		Endpoints: []discoveryv1.Endpoint{{
   106  			Addresses: []string{
   107  				"1.2.3.4",
   108  				"4.3.2.1",
   109  			},
   110  		}},
   111  		Ports: []discoveryv1.EndpointPort{{
   112  			Port:     ptr.To[int32](8080),
   113  			Protocol: ptr.To(v1.ProtocolTCP),
   114  		}},
   115  	}
   116  	endpoints2 := &discoveryv1.EndpointSlice{
   117  		ObjectMeta:  metav1.ObjectMeta{Namespace: "testnamespace", Name: "e2"},
   118  		AddressType: discoveryv1.AddressTypeIPv4,
   119  		Endpoints: []discoveryv1.Endpoint{{
   120  			Addresses: []string{
   121  				"5.6.7.8",
   122  			},
   123  		}},
   124  		Ports: []discoveryv1.EndpointPort{{
   125  			Port:     ptr.To[int32](8080),
   126  			Protocol: ptr.To(v1.ProtocolTCP),
   127  		}},
   128  	}
   129  
   130  	// Setup fake api client.
   131  	client := fake.NewSimpleClientset()
   132  	fakeWatch := watch.NewFake()
   133  	client.PrependWatchReactor("endpointslices", ktesting.DefaultWatchReactor(fakeWatch, nil))
   134  
   135  	stopCh := make(chan struct{})
   136  	defer close(stopCh)
   137  
   138  	handler := NewEndpointSliceHandlerMock()
   139  
   140  	sharedInformers := informers.NewSharedInformerFactory(client, time.Minute)
   141  
   142  	endpointsliceConfig := NewEndpointSliceConfig(ctx, sharedInformers.Discovery().V1().EndpointSlices(), time.Minute)
   143  	endpointsliceConfig.RegisterEventHandler(handler)
   144  	go sharedInformers.Start(stopCh)
   145  	go endpointsliceConfig.Run(stopCh)
   146  
   147  	// Add the first endpoints
   148  	fakeWatch.Add(endpoints1v1)
   149  	handler.ValidateEndpointSlices(t, []*discoveryv1.EndpointSlice{endpoints1v1})
   150  
   151  	// Add another endpoints
   152  	fakeWatch.Add(endpoints2)
   153  	handler.ValidateEndpointSlices(t, []*discoveryv1.EndpointSlice{endpoints1v1, endpoints2})
   154  
   155  	// Modify endpoints1
   156  	fakeWatch.Modify(endpoints1v2)
   157  	handler.ValidateEndpointSlices(t, []*discoveryv1.EndpointSlice{endpoints1v2, endpoints2})
   158  
   159  	// Delete endpoints1
   160  	fakeWatch.Delete(endpoints1v2)
   161  	handler.ValidateEndpointSlices(t, []*discoveryv1.EndpointSlice{endpoints2})
   162  
   163  	// Delete endpoints2
   164  	fakeWatch.Delete(endpoints2)
   165  	handler.ValidateEndpointSlices(t, []*discoveryv1.EndpointSlice{})
   166  }
   167  
   168  func TestInitialSync(t *testing.T) {
   169  	_, ctx := klogtesting.NewTestContext(t)
   170  	svc1 := &v1.Service{
   171  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
   172  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Protocol: "TCP", Port: 10}}},
   173  	}
   174  	svc2 := &v1.Service{
   175  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
   176  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Protocol: "TCP", Port: 10}}},
   177  	}
   178  	eps1 := &discoveryv1.EndpointSlice{
   179  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
   180  	}
   181  	eps2 := &discoveryv1.EndpointSlice{
   182  		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
   183  	}
   184  
   185  	expectedSvcState := map[types.NamespacedName]*v1.Service{
   186  		{Name: svc1.Name, Namespace: svc1.Namespace}: svc1,
   187  		{Name: svc2.Name, Namespace: svc2.Namespace}: svc2,
   188  	}
   189  	expectedEpsState := map[types.NamespacedName]*discoveryv1.EndpointSlice{
   190  		{Name: eps1.Name, Namespace: eps1.Namespace}: eps1,
   191  		{Name: eps2.Name, Namespace: eps2.Namespace}: eps2,
   192  	}
   193  
   194  	// Setup fake api client.
   195  	client := fake.NewSimpleClientset(svc1, svc2, eps2, eps1)
   196  	sharedInformers := informers.NewSharedInformerFactory(client, 0)
   197  
   198  	svcConfig := NewServiceConfig(ctx, sharedInformers.Core().V1().Services(), 0)
   199  	svcHandler := NewServiceHandlerMock()
   200  	svcConfig.RegisterEventHandler(svcHandler)
   201  
   202  	epsConfig := NewEndpointSliceConfig(ctx, sharedInformers.Discovery().V1().EndpointSlices(), 0)
   203  	epsHandler := NewEndpointSliceHandlerMock()
   204  	epsConfig.RegisterEventHandler(epsHandler)
   205  
   206  	stopCh := make(chan struct{})
   207  	defer close(stopCh)
   208  	sharedInformers.Start(stopCh)
   209  
   210  	err := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
   211  		svcHandler.lock.Lock()
   212  		defer svcHandler.lock.Unlock()
   213  		if reflect.DeepEqual(svcHandler.state, expectedSvcState) {
   214  			return true, nil
   215  		}
   216  		return false, nil
   217  	})
   218  	if err != nil {
   219  		t.Fatal("Timed out waiting for the completion of handler `OnServiceAdd`")
   220  	}
   221  
   222  	err = wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
   223  		epsHandler.lock.Lock()
   224  		defer epsHandler.lock.Unlock()
   225  		if reflect.DeepEqual(epsHandler.state, expectedEpsState) {
   226  			return true, nil
   227  		}
   228  		return false, nil
   229  	})
   230  	if err != nil {
   231  		t.Fatal("Timed out waiting for the completion of handler `OnEndpointsAdd`")
   232  	}
   233  
   234  	svcConfig.Run(stopCh)
   235  	epsConfig.Run(stopCh)
   236  
   237  	gotSvc := <-svcHandler.updated
   238  	gotSvcState := make(map[types.NamespacedName]*v1.Service, len(gotSvc))
   239  	for _, svc := range gotSvc {
   240  		gotSvcState[types.NamespacedName{Namespace: svc.Namespace, Name: svc.Name}] = svc
   241  	}
   242  	if !reflect.DeepEqual(gotSvcState, expectedSvcState) {
   243  		t.Fatalf("Expected service state: %v\nGot: %v\n", expectedSvcState, gotSvcState)
   244  	}
   245  
   246  	gotEps := <-epsHandler.updated
   247  	gotEpsState := make(map[types.NamespacedName]*discoveryv1.EndpointSlice, len(gotEps))
   248  	for _, eps := range gotEps {
   249  		gotEpsState[types.NamespacedName{Namespace: eps.Namespace, Name: eps.Name}] = eps
   250  	}
   251  	if !reflect.DeepEqual(gotEpsState, expectedEpsState) {
   252  		t.Fatalf("Expected endpoints state: %v\nGot: %v\n", expectedEpsState, gotEpsState)
   253  	}
   254  }