| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 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" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 	v1 "k8s.io/api/core/v1" | 
					
						
							|  |  |  | 	discovery "k8s.io/api/discovery/v1" | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime" | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/types" | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 	"k8s.io/utils/pointer" | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(80), pointer.Int32(443)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		"2 slices, same port": { | 
					
						
							|  |  |  | 			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"}, | 
					
						
							|  |  |  | 			endpointSlices: []*discovery.EndpointSlice{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns1", 2, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// 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{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 4, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// 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{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 3, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 6, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 		// 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{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 3, 5, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 6, 5, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			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}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		"2 slices, overlapping endpoints, all unready": { | 
					
						
							|  |  |  | 			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"}, | 
					
						
							|  |  |  | 			endpointSlices: []*discovery.EndpointSlice{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 1, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 10, 1, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			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}, | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		"3 slices with different services and namespaces": { | 
					
						
							|  |  |  | 			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"}, | 
					
						
							|  |  |  | 			endpointSlices: []*discovery.EndpointSlice{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc2", "ns1", 2, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSlice("svc1", "ns2", 3, 3, 999, 999, []string{}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 					&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}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// 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{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{nil}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: map[ServicePortName][]*BaseEndpointInfo{}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 		// 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{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSliceWithOffset("svc1", "ns1", 1, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSliceWithOffset("svc1", "ns1", 2, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(8080)}), | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			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}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for name, tc := range testCases { | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 		t.Run(name, func(t *testing.T) { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			esCache := NewEndpointSliceCache(tc.hostname, v1.IPv4Protocol, nil, nil) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 			cmc := newCacheMutationCheck(tc.endpointSlices) | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 			for _, endpointSlice := range tc.endpointSlices { | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 				esCache.updatePending(endpointSlice, false) | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			compareEndpointsMapsStr(t, esCache.getEndpointsMap(tc.namespacedName, esCache.trackerByServiceMap[tc.namespacedName].pending), tc.expectedMap) | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 			cmc.Check(t) | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestEndpointInfoByServicePort(t *testing.T) { | 
					
						
							|  |  |  | 	testCases := map[string]struct { | 
					
						
							|  |  |  | 		namespacedName types.NamespacedName | 
					
						
							|  |  |  | 		endpointSlices []*discovery.EndpointSlice | 
					
						
							|  |  |  | 		hostname       string | 
					
						
							| 
									
										
										
										
											2019-09-06 07:46:58 +08:00
										 |  |  | 		expectedMap    spToEndpointMap | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		"simple use case with 3 endpoints": { | 
					
						
							|  |  |  | 			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"}, | 
					
						
							|  |  |  | 			hostname:       "host1", | 
					
						
							|  |  |  | 			endpointSlices: []*discovery.EndpointSlice{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(80)}), | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-09-06 07:46:58 +08:00
										 |  |  | 			expectedMap: spToEndpointMap{ | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 					"10.0.1.1:80": &BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Endpoint:    "10.0.1.1:80", | 
					
						
							|  |  |  | 						IsLocal:     false, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host2", | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 					"10.0.1.2:80": &BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Endpoint:    "10.0.1.2:80", | 
					
						
							|  |  |  | 						IsLocal:     true, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host1", | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 					"10.0.1.3:80": &BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Endpoint:    "10.0.1.3:80", | 
					
						
							|  |  |  | 						IsLocal:     false, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host2", | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 		"4 different slices with duplicate IPs": { | 
					
						
							|  |  |  | 			namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"}, | 
					
						
							|  |  |  | 			hostname:       "host1", | 
					
						
							|  |  |  | 			endpointSlices: []*discovery.EndpointSlice{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 				generateEndpointSliceWithOffset("svc1", "ns1", 1, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(80)}), | 
					
						
							|  |  |  | 				generateEndpointSliceWithOffset("svc1", "ns1", 2, 1, 2, 999, 999, []string{"host1", "host2"}, []*int32{pointer.Int32(8080)}), | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expectedMap: spToEndpointMap{ | 
					
						
							|  |  |  | 				makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): { | 
					
						
							|  |  |  | 					"10.0.1.1:80": &BaseEndpointInfo{ | 
					
						
							|  |  |  | 						Endpoint:    "10.0.1.1:80", | 
					
						
							|  |  |  | 						IsLocal:     false, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host2", | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					"10.0.1.2:80": &BaseEndpointInfo{ | 
					
						
							|  |  |  | 						Endpoint:    "10.0.1.2:80", | 
					
						
							|  |  |  | 						IsLocal:     true, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host1", | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					"10.0.1.1:8080": &BaseEndpointInfo{ | 
					
						
							|  |  |  | 						Endpoint:    "10.0.1.1:8080", | 
					
						
							|  |  |  | 						IsLocal:     false, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host2", | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					"10.0.1.2:8080": &BaseEndpointInfo{ | 
					
						
							|  |  |  | 						Endpoint:    "10.0.1.2:8080", | 
					
						
							|  |  |  | 						IsLocal:     true, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 						NodeName:    "host1", | 
					
						
							| 
									
										
										
										
											2021-05-24 12:40:41 +08:00
										 |  |  | 						Ready:       true, | 
					
						
							|  |  |  | 						Serving:     true, | 
					
						
							|  |  |  | 						Terminating: false, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for name, tc := range testCases { | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 		t.Run(name, func(t *testing.T) { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			esCache := NewEndpointSliceCache(tc.hostname, v1.IPv4Protocol, nil, nil) | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			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) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | func TestEsInfoChanged(t *testing.T) { | 
					
						
							|  |  |  | 	p80 := int32(80) | 
					
						
							|  |  |  | 	p443 := int32(443) | 
					
						
							|  |  |  | 	tcpProto := v1.ProtocolTCP | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 	port80 := discovery.EndpointPort{Port: &p80, Name: pointer.String("http"), Protocol: &tcpProto} | 
					
						
							|  |  |  | 	port443 := discovery.EndpointPort{Port: &p443, Name: pointer.String("https"), Protocol: &tcpProto} | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	endpoint1 := discovery.Endpoint{Addresses: []string{"10.0.1.0"}} | 
					
						
							|  |  |  | 	endpoint2 := discovery.Endpoint{Addresses: []string{"10.0.1.1"}} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	objMeta := metav1.ObjectMeta{ | 
					
						
							|  |  |  | 		Name:      "foo", | 
					
						
							|  |  |  | 		Namespace: "bar", | 
					
						
							|  |  |  | 		Labels:    map[string]string{discovery.LabelServiceName: "svc1"}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	testCases := map[string]struct { | 
					
						
							|  |  |  | 		cache         *EndpointSliceCache | 
					
						
							|  |  |  | 		initialSlice  *discovery.EndpointSlice | 
					
						
							|  |  |  | 		updatedSlice  *discovery.EndpointSlice | 
					
						
							|  |  |  | 		expectChanged bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		"identical slices, ports only": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			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": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			initialSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port443, port80}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			updatedSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port80, port443}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectChanged: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		"port removed": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			initialSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port443, port80}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			updatedSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port443}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectChanged: true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		"port added": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			initialSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port443}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			updatedSlice: &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 				ObjectMeta: objMeta, | 
					
						
							|  |  |  | 				Ports:      []discovery.EndpointPort{port443, port80}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectChanged: true, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		"identical with endpoints": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			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": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			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": { | 
					
						
							| 
									
										
											  
											
												dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
											
										 
											2020-10-27 04:15:59 +08:00
										 |  |  | 			cache: NewEndpointSliceCache("", v1.IPv4Protocol, nil, nil), | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			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) { | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 			cmc := newCacheMutationCheck([]*discovery.EndpointSlice{tc.initialSlice}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			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) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			cmc.Check(t) | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | func generateEndpointSliceWithOffset(serviceName, namespace string, sliceNum, offset, numEndpoints, unreadyMod int, terminatingMod int, hosts []string, portNums []*int32) *discovery.EndpointSlice { | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 	tcpProtocol := v1.ProtocolTCP | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	endpointSlice := &discovery.EndpointSlice{ | 
					
						
							|  |  |  | 		ObjectMeta: metav1.ObjectMeta{ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:12:15 +08:00
										 |  |  | 			Name:      fmt.Sprintf("%s-%d", serviceName, sliceNum), | 
					
						
							|  |  |  | 			Namespace: namespace, | 
					
						
							|  |  |  | 			Labels:    map[string]string{discovery.LabelServiceName: serviceName}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Ports:       []discovery.EndpointPort{}, | 
					
						
							| 
									
										
										
										
											2019-11-07 14:46:04 +08:00
										 |  |  | 		AddressType: discovery.AddressTypeIPv4, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		Endpoints:   []discovery.Endpoint{}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, portNum := range portNums { | 
					
						
							|  |  |  | 		endpointSlice.Ports = append(endpointSlice.Ports, discovery.EndpointPort{ | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 			Name:     pointer.String(fmt.Sprintf("port-%d", i)), | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 			Port:     portNum, | 
					
						
							|  |  |  | 			Protocol: &tcpProtocol, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 1; i <= numEndpoints; i++ { | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 		readyCondition := i%unreadyMod != 0 | 
					
						
							|  |  |  | 		terminatingCondition := i%terminatingMod == 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 23:15:32 +08:00
										 |  |  | 		ready := pointer.Bool(readyCondition && !terminatingCondition) | 
					
						
							|  |  |  | 		serving := pointer.Bool(readyCondition) | 
					
						
							|  |  |  | 		terminating := pointer.Bool(terminatingCondition) | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		endpoint := discovery.Endpoint{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 			Addresses: []string{fmt.Sprintf("10.0.%d.%d", offset, i)}, | 
					
						
							|  |  |  | 			Conditions: discovery.EndpointConditions{ | 
					
						
							|  |  |  | 				Ready:       ready, | 
					
						
							|  |  |  | 				Serving:     serving, | 
					
						
							|  |  |  | 				Terminating: terminating, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(hosts) > 0 { | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 			hostname := hosts[i%len(hosts)] | 
					
						
							|  |  |  | 			endpoint.NodeName = &hostname | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		endpointSlice.Endpoints = append(endpointSlice.Endpoints, endpoint) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return endpointSlice | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-12-10 06:20:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |