mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	* Use k8s.io/utils/ptr in pkg/proxy * Replace pointer.String(), pointer.StringPtr(), and pointer.Bool() with ptr.To() * Replace pointer.Int32(constexpr) with ptr.To[int32](constexpr) * Replace pointer.Int32(int32(var)) with ptr.To(int32(var)) * Replace remaining pointer.Int32() cases with ptr.To * Replace 'tcpProtocol := v1.ProtocolTCP; ... &tcpProtocol', etc with ptr.To(v1.ProtocolTCP) * Replace 'nodeName = testHostname; ... &nodeName' with ptr.To(testHostname) * Use ptr.To for SessionAffinityConfig.ClientIP.TimeoutSeconds * Use ptr.To for InternalTrafficPolicy * Use ptr.To for LoadBalancer.Ingress.IPMode
		
			
				
	
	
		
			548 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			548 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2019 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 proxy
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	v1 "k8s.io/api/core/v1"
 | 
						|
	discovery "k8s.io/api/discovery/v1"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime"
 | 
						|
	"k8s.io/apimachinery/pkg/types"
 | 
						|
	"k8s.io/utils/ptr"
 | 
						|
)
 | 
						|
 | 
						|
func TestEndpointsMapFromESC(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		endpointSlices []*discovery.EndpointSlice
 | 
						|
		hostname       string
 | 
						|
		namespacedName types.NamespacedName
 | 
						|
		expectedMap    map[ServicePortName][]*BaseEndpointInfo
 | 
						|
	}{
 | 
						|
		"1 slice, 2 hosts, ports 80,443": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			hostname:       "host1",
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
				makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:443", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:443", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"2 slices, same port": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns1", 2, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.2.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.2.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.2.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		// 2 slices, with some overlapping endpoints, result should be a union
 | 
						|
		// of the 2.
 | 
						|
		"2 overlapping slices, same port": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 4, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.4:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		// 2 slices with all endpoints overlapping, more unready in first than
 | 
						|
		// second. If an endpoint is marked ready, we add it to the
 | 
						|
		// EndpointsMap, even if conditions.Ready isn't true for another
 | 
						|
		// matching endpoint
 | 
						|
		"2 slices, overlapping endpoints, some endpoints unready in 1 or both": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 3, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 6, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.10:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.4:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.5:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.6:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.7:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.8:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.9:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		// 2 slices with all endpoints overlapping, some unready and terminating.
 | 
						|
		"2 slices, overlapping endpoints, some endpoints unready and some endpoints terminating": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 3, 5, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 6, 5, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.10:80", isLocal: false, ready: false, serving: true, terminating: true},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.4:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.5:80", isLocal: false, ready: false, serving: true, terminating: true},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.6:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.7:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.8:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.9:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"2 slices, overlapping endpoints, all unready": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 1, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 10, 1, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.10:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.4:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.5:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.6:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.7:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.8:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.9:80", isLocal: false, ready: false, serving: false, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"3 slices with different services and namespaces": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc2", "ns1", 2, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSlice("svc1", "ns2", 3, 3, 999, 999, []string{}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		// Ensuring that nil port value will not break things. This will
 | 
						|
		// represent all ports in the future, but that has not been implemented
 | 
						|
		// yet.
 | 
						|
		"Nil port should not break anything": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			hostname:       "host1",
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{nil}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{},
 | 
						|
		},
 | 
						|
		// Make sure that different endpoints with duplicate IPs are returned correctly.
 | 
						|
		"Different endpoints with duplicate IPs should not be filtered": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			hostname:       "host1",
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSliceWithOffset("svc1", "ns1", 1, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSliceWithOffset("svc1", "ns1", 2, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](8080)}),
 | 
						|
			},
 | 
						|
			expectedMap: map[ServicePortName][]*BaseEndpointInfo{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.1:8080", isLocal: false, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
 | 
						|
					&BaseEndpointInfo{endpoint: "10.0.1.2:8080", isLocal: true, ready: true, serving: true, terminating: false},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for name, tc := range testCases {
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			esCache := NewEndpointSliceCache(tc.hostname, v1.IPv4Protocol, nil, nil)
 | 
						|
 | 
						|
			cmc := newCacheMutationCheck(tc.endpointSlices)
 | 
						|
			for _, endpointSlice := range tc.endpointSlices {
 | 
						|
				esCache.updatePending(endpointSlice, false)
 | 
						|
			}
 | 
						|
 | 
						|
			compareEndpointsMapsStr(t, esCache.getEndpointsMap(tc.namespacedName, esCache.trackerByServiceMap[tc.namespacedName].pending), tc.expectedMap)
 | 
						|
			cmc.Check(t)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestEndpointInfoByServicePort(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		namespacedName types.NamespacedName
 | 
						|
		endpointSlices []*discovery.EndpointSlice
 | 
						|
		hostname       string
 | 
						|
		expectedMap    spToEndpointMap
 | 
						|
	}{
 | 
						|
		"simple use case with 3 endpoints": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			hostname:       "host1",
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80)}),
 | 
						|
			},
 | 
						|
			expectedMap: spToEndpointMap{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					"10.0.1.1:80": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.1:80",
 | 
						|
						isLocal:     false,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
					"10.0.1.2:80": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.2:80",
 | 
						|
						isLocal:     true,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
					"10.0.1.3:80": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.3:80",
 | 
						|
						isLocal:     false,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"4 different slices with duplicate IPs": {
 | 
						|
			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
 | 
						|
			hostname:       "host1",
 | 
						|
			endpointSlices: []*discovery.EndpointSlice{
 | 
						|
				generateEndpointSliceWithOffset("svc1", "ns1", 1, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80)}),
 | 
						|
				generateEndpointSliceWithOffset("svc1", "ns1", 2, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](8080)}),
 | 
						|
			},
 | 
						|
			expectedMap: spToEndpointMap{
 | 
						|
				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
 | 
						|
					"10.0.1.1:80": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.1:80",
 | 
						|
						isLocal:     false,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
					"10.0.1.2:80": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.2:80",
 | 
						|
						isLocal:     true,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
					"10.0.1.1:8080": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.1:8080",
 | 
						|
						isLocal:     false,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
					"10.0.1.2:8080": &BaseEndpointInfo{
 | 
						|
						endpoint:    "10.0.1.2:8080",
 | 
						|
						isLocal:     true,
 | 
						|
						ready:       true,
 | 
						|
						serving:     true,
 | 
						|
						terminating: false,
 | 
						|
					},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for name, tc := range testCases {
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			esCache := NewEndpointSliceCache(tc.hostname, v1.IPv4Protocol, nil, nil)
 | 
						|
 | 
						|
			for _, endpointSlice := range tc.endpointSlices {
 | 
						|
				esCache.updatePending(endpointSlice, false)
 | 
						|
			}
 | 
						|
 | 
						|
			got := esCache.endpointInfoByServicePort(tc.namespacedName, esCache.trackerByServiceMap[tc.namespacedName].pending)
 | 
						|
			if !reflect.DeepEqual(got, tc.expectedMap) {
 | 
						|
				t.Errorf("endpointInfoByServicePort does not match. Want: %+v, Got: %+v", tc.expectedMap, got)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestEsInfoChanged(t *testing.T) {
 | 
						|
	p80 := int32(80)
 | 
						|
	p443 := int32(443)
 | 
						|
	port80 := discovery.EndpointPort{Port: &p80, Name: ptr.To("http"), Protocol: ptr.To(v1.ProtocolTCP)}
 | 
						|
	port443 := discovery.EndpointPort{Port: &p443, Name: ptr.To("https"), Protocol: ptr.To(v1.ProtocolTCP)}
 | 
						|
	endpoint1 := discovery.Endpoint{Addresses: []string{"10.0.1.0"}}
 | 
						|
	endpoint2 := discovery.Endpoint{Addresses: []string{"10.0.1.1"}}
 | 
						|
 | 
						|
	objMeta := metav1.ObjectMeta{
 | 
						|
		Name:      "foo",
 | 
						|
		Namespace: "bar",
 | 
						|
		Labels:    map[string]string{discovery.LabelServiceName: "svc1"},
 | 
						|
	}
 | 
						|
 | 
						|
	testCases := map[string]struct {
 | 
						|
		cache         *EndpointSliceCache
 | 
						|
		initialSlice  *discovery.EndpointSlice
 | 
						|
		updatedSlice  *discovery.EndpointSlice
 | 
						|
		expectChanged bool
 | 
						|
	}{
 | 
						|
		"identical slices, ports only": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port80},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port80},
 | 
						|
			},
 | 
						|
			expectChanged: false,
 | 
						|
		},
 | 
						|
		"identical slices, ports out of order": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443, port80},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port80, port443},
 | 
						|
			},
 | 
						|
			expectChanged: false,
 | 
						|
		},
 | 
						|
		"port removed": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443, port80},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
			},
 | 
						|
			expectChanged: true,
 | 
						|
		},
 | 
						|
		"port added": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443, port80},
 | 
						|
			},
 | 
						|
			expectChanged: true,
 | 
						|
		},
 | 
						|
		"identical with endpoints": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint1, endpoint2},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint1, endpoint2},
 | 
						|
			},
 | 
						|
			expectChanged: false,
 | 
						|
		},
 | 
						|
		"identical with endpoints out of order": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint1, endpoint2},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint2, endpoint1},
 | 
						|
			},
 | 
						|
			expectChanged: false,
 | 
						|
		},
 | 
						|
		"identical with endpoint added": {
 | 
						|
			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil),
 | 
						|
			initialSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint1},
 | 
						|
			},
 | 
						|
			updatedSlice: &discovery.EndpointSlice{
 | 
						|
				ObjectMeta: objMeta,
 | 
						|
				Ports:      []discovery.EndpointPort{port443},
 | 
						|
				Endpoints:  []discovery.Endpoint{endpoint2, endpoint1},
 | 
						|
			},
 | 
						|
			expectChanged: true,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for name, tc := range testCases {
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			cmc := newCacheMutationCheck([]*discovery.EndpointSlice{tc.initialSlice})
 | 
						|
 | 
						|
			if tc.initialSlice != nil {
 | 
						|
				tc.cache.updatePending(tc.initialSlice, false)
 | 
						|
				tc.cache.checkoutChanges()
 | 
						|
			}
 | 
						|
 | 
						|
			serviceKey, sliceKey, err := endpointSliceCacheKeys(tc.updatedSlice)
 | 
						|
			if err != nil {
 | 
						|
				t.Fatalf("Expected no error calling endpointSliceCacheKeys(): %v", err)
 | 
						|
			}
 | 
						|
 | 
						|
			esInfo := newEndpointSliceInfo(tc.updatedSlice, false)
 | 
						|
			changed := tc.cache.esInfoChanged(serviceKey, sliceKey, esInfo)
 | 
						|
 | 
						|
			if tc.expectChanged != changed {
 | 
						|
				t.Errorf("Expected esInfoChanged() to return %t, got %t", tc.expectChanged, changed)
 | 
						|
			}
 | 
						|
 | 
						|
			cmc.Check(t)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func generateEndpointSliceWithOffset(serviceName, namespace string, sliceNum, offset, numEndpoints, unreadyMod int, terminatingMod int, hosts []string, portNums []*int32) *discovery.EndpointSlice {
 | 
						|
	endpointSlice := &discovery.EndpointSlice{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Name:      fmt.Sprintf("%s-%d", serviceName, sliceNum),
 | 
						|
			Namespace: namespace,
 | 
						|
			Labels:    map[string]string{discovery.LabelServiceName: serviceName},
 | 
						|
		},
 | 
						|
		Ports:       []discovery.EndpointPort{},
 | 
						|
		AddressType: discovery.AddressTypeIPv4,
 | 
						|
		Endpoints:   []discovery.Endpoint{},
 | 
						|
	}
 | 
						|
 | 
						|
	for i, portNum := range portNums {
 | 
						|
		endpointSlice.Ports = append(endpointSlice.Ports, discovery.EndpointPort{
 | 
						|
			Name:     ptr.To(fmt.Sprintf("port-%d", i)),
 | 
						|
			Port:     portNum,
 | 
						|
			Protocol: ptr.To(v1.ProtocolTCP),
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 1; i <= numEndpoints; i++ {
 | 
						|
		readyCondition := i%unreadyMod != 0
 | 
						|
		terminatingCondition := i%terminatingMod == 0
 | 
						|
 | 
						|
		ready := ptr.To(readyCondition && !terminatingCondition)
 | 
						|
		serving := ptr.To(readyCondition)
 | 
						|
		terminating := ptr.To(terminatingCondition)
 | 
						|
 | 
						|
		endpoint := discovery.Endpoint{
 | 
						|
			Addresses: []string{fmt.Sprintf("10.0.%d.%d", offset, i)},
 | 
						|
			Conditions: discovery.EndpointConditions{
 | 
						|
				Ready:       ready,
 | 
						|
				Serving:     serving,
 | 
						|
				Terminating: terminating,
 | 
						|
			},
 | 
						|
		}
 | 
						|
 | 
						|
		if len(hosts) > 0 {
 | 
						|
			hostname := hosts[i%len(hosts)]
 | 
						|
			endpoint.NodeName = &hostname
 | 
						|
		}
 | 
						|
 | 
						|
		endpointSlice.Endpoints = append(endpointSlice.Endpoints, endpoint)
 | 
						|
	}
 | 
						|
 | 
						|
	return endpointSlice
 | 
						|
}
 | 
						|
 | 
						|
func generateEndpointSlice(serviceName, namespace string, sliceNum, numEndpoints, unreadyMod int, terminatingMod int, hosts []string, portNums []*int32) *discovery.EndpointSlice {
 | 
						|
	return generateEndpointSliceWithOffset(serviceName, namespace, sliceNum, sliceNum, numEndpoints, unreadyMod, terminatingMod, hosts, portNums)
 | 
						|
}
 | 
						|
 | 
						|
// cacheMutationCheck helps ensure that cached objects have not been changed
 | 
						|
// in any way throughout a test run.
 | 
						|
type cacheMutationCheck struct {
 | 
						|
	objects []cacheObject
 | 
						|
}
 | 
						|
 | 
						|
// cacheObject stores a reference to an original object as well as a deep copy
 | 
						|
// of that object to track any mutations in the original object.
 | 
						|
type cacheObject struct {
 | 
						|
	original runtime.Object
 | 
						|
	deepCopy runtime.Object
 | 
						|
}
 | 
						|
 | 
						|
// newCacheMutationCheck initializes a cacheMutationCheck with EndpointSlices.
 | 
						|
func newCacheMutationCheck(endpointSlices []*discovery.EndpointSlice) cacheMutationCheck {
 | 
						|
	cmc := cacheMutationCheck{}
 | 
						|
	for _, endpointSlice := range endpointSlices {
 | 
						|
		cmc.Add(endpointSlice)
 | 
						|
	}
 | 
						|
	return cmc
 | 
						|
}
 | 
						|
 | 
						|
// Add appends a runtime.Object and a deep copy of that object into the
 | 
						|
// cacheMutationCheck.
 | 
						|
func (cmc *cacheMutationCheck) Add(o runtime.Object) {
 | 
						|
	cmc.objects = append(cmc.objects, cacheObject{
 | 
						|
		original: o,
 | 
						|
		deepCopy: o.DeepCopyObject(),
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// Check verifies that no objects in the cacheMutationCheck have been mutated.
 | 
						|
func (cmc *cacheMutationCheck) Check(t *testing.T) {
 | 
						|
	for _, o := range cmc.objects {
 | 
						|
		if !reflect.DeepEqual(o.original, o.deepCopy) {
 | 
						|
			// Cached objects can't be safely mutated and instead should be deep
 | 
						|
			// copied before changed in any way.
 | 
						|
			t.Errorf("Cached object was unexpectedly mutated. Original: %+v, Mutated: %+v", o.deepCopy, o.original)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |