1
16
17 package proxy
18
19 import (
20 "fmt"
21 "reflect"
22 "testing"
23 "time"
24
25 v1 "k8s.io/api/core/v1"
26 discovery "k8s.io/api/discovery/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/types"
29 "k8s.io/apimachinery/pkg/util/sets"
30 "k8s.io/utils/ptr"
31 )
32
33 func (proxier *FakeProxier) addEndpointSlice(slice *discovery.EndpointSlice) {
34 proxier.endpointsChanges.EndpointSliceUpdate(slice, false)
35 }
36
37 func (proxier *FakeProxier) updateEndpointSlice(oldSlice, slice *discovery.EndpointSlice) {
38 proxier.endpointsChanges.EndpointSliceUpdate(slice, false)
39 }
40
41 func (proxier *FakeProxier) deleteEndpointSlice(slice *discovery.EndpointSlice) {
42 proxier.endpointsChanges.EndpointSliceUpdate(slice, true)
43 }
44
45 func TestGetLocalEndpointIPs(t *testing.T) {
46 testCases := []struct {
47 endpointsMap EndpointsMap
48 expected map[types.NamespacedName]sets.Set[string]
49 }{{
50
51 endpointsMap: EndpointsMap{},
52 expected: map[types.NamespacedName]sets.Set[string]{},
53 }, {
54
55 endpointsMap: EndpointsMap{
56 makeServicePortName("ns1", "ep1", "", v1.ProtocolTCP): []Endpoint{
57 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
58 },
59 },
60 expected: map[types.NamespacedName]sets.Set[string]{},
61 }, {
62
63 endpointsMap: EndpointsMap{
64 makeServicePortName("ns1", "ep1", "", v1.ProtocolTCP): []Endpoint{
65 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
66 },
67 },
68 expected: map[types.NamespacedName]sets.Set[string]{
69 {Namespace: "ns1", Name: "ep1"}: sets.New[string]("1.1.1.1"),
70 },
71 }, {
72
73 endpointsMap: EndpointsMap{
74 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolTCP): []Endpoint{
75 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
76 &BaseEndpointInfo{ip: "1.1.1.2", port: 11, endpoint: "1.1.1.2:11", isLocal: true, ready: true, serving: true, terminating: false},
77 },
78 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolTCP): []Endpoint{
79 &BaseEndpointInfo{ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: false, ready: true, serving: true, terminating: false},
80 &BaseEndpointInfo{ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
81 },
82 },
83 expected: map[types.NamespacedName]sets.Set[string]{
84 {Namespace: "ns1", Name: "ep1"}: sets.New[string]("1.1.1.2"),
85 },
86 }, {
87
88 endpointsMap: EndpointsMap{
89 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolTCP): []Endpoint{
90 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
91 },
92 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolTCP): []Endpoint{
93 &BaseEndpointInfo{ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: true, serving: true, terminating: false},
94 &BaseEndpointInfo{ip: "2.2.2.22", port: 22, endpoint: "2.2.2.22:22", isLocal: true, ready: true, serving: true, terminating: false},
95 },
96 makeServicePortName("ns2", "ep2", "p23", v1.ProtocolTCP): []Endpoint{
97 &BaseEndpointInfo{ip: "2.2.2.3", port: 23, endpoint: "2.2.2.3:23", isLocal: true, ready: true, serving: true, terminating: false},
98 },
99 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolTCP): []Endpoint{
100 &BaseEndpointInfo{ip: "4.4.4.4", port: 44, endpoint: "4.4.4.4:44", isLocal: true, ready: true, serving: true, terminating: false},
101 &BaseEndpointInfo{ip: "4.4.4.5", port: 44, endpoint: "4.4.4.5:44", isLocal: false, ready: true, serving: true, terminating: false},
102 },
103 makeServicePortName("ns4", "ep4", "p45", v1.ProtocolTCP): []Endpoint{
104 &BaseEndpointInfo{ip: "4.4.4.6", port: 45, endpoint: "4.4.4.6:45", isLocal: true, ready: true, serving: true, terminating: false},
105 },
106 },
107 expected: map[types.NamespacedName]sets.Set[string]{
108 {Namespace: "ns2", Name: "ep2"}: sets.New[string]("2.2.2.2", "2.2.2.22", "2.2.2.3"),
109 {Namespace: "ns4", Name: "ep4"}: sets.New[string]("4.4.4.4", "4.4.4.6"),
110 },
111 }, {
112
113 endpointsMap: EndpointsMap{
114 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolTCP): []Endpoint{
115 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
116 },
117 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolTCP): []Endpoint{
118 &BaseEndpointInfo{ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: true, serving: true, terminating: false},
119 &BaseEndpointInfo{ip: "2.2.2.22", port: 22, endpoint: "2.2.2.22:22", isLocal: true, ready: true, serving: true, terminating: false},
120 },
121 makeServicePortName("ns2", "ep2", "p23", v1.ProtocolTCP): []Endpoint{
122 &BaseEndpointInfo{ip: "2.2.2.3", port: 23, endpoint: "2.2.2.3:23", isLocal: true, ready: false, serving: true, terminating: true},
123 },
124 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolTCP): []Endpoint{
125 &BaseEndpointInfo{ip: "4.4.4.4", port: 44, endpoint: "4.4.4.4:44", isLocal: true, ready: true, serving: true, terminating: false},
126 &BaseEndpointInfo{ip: "4.4.4.5", port: 44, endpoint: "4.4.4.5:44", isLocal: false, ready: true, serving: true, terminating: false},
127 },
128 makeServicePortName("ns4", "ep4", "p45", v1.ProtocolTCP): []Endpoint{
129 &BaseEndpointInfo{ip: "4.4.4.6", port: 45, endpoint: "4.4.4.6:45", isLocal: true, ready: true, serving: true, terminating: false},
130 },
131 },
132 expected: map[types.NamespacedName]sets.Set[string]{
133 {Namespace: "ns2", Name: "ep2"}: sets.New[string]("2.2.2.2", "2.2.2.22"),
134 {Namespace: "ns4", Name: "ep4"}: sets.New[string]("4.4.4.4", "4.4.4.6"),
135 },
136 }, {
137
138 endpointsMap: EndpointsMap{
139 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolTCP): []Endpoint{
140 &BaseEndpointInfo{ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: false, serving: true, terminating: true},
141 },
142 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolTCP): []Endpoint{
143 &BaseEndpointInfo{ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: false, serving: true, terminating: true},
144 &BaseEndpointInfo{ip: "2.2.2.22", port: 22, endpoint: "2.2.2.22:22", isLocal: true, ready: false, serving: true, terminating: true},
145 },
146 makeServicePortName("ns2", "ep2", "p23", v1.ProtocolTCP): []Endpoint{
147 &BaseEndpointInfo{ip: "2.2.2.3", port: 23, endpoint: "2.2.2.3:23", isLocal: true, ready: false, serving: true, terminating: true},
148 },
149 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolTCP): []Endpoint{
150 &BaseEndpointInfo{ip: "4.4.4.4", port: 44, endpoint: "4.4.4.4:44", isLocal: true, ready: false, serving: true, terminating: true},
151 &BaseEndpointInfo{ip: "4.4.4.5", port: 44, endpoint: "4.4.4.5:44", isLocal: false, ready: false, serving: true, terminating: true},
152 },
153 makeServicePortName("ns4", "ep4", "p45", v1.ProtocolTCP): []Endpoint{
154 &BaseEndpointInfo{ip: "4.4.4.6", port: 45, endpoint: "4.4.4.6:45", isLocal: true, ready: false, serving: true, terminating: true},
155 },
156 },
157 expected: make(map[types.NamespacedName]sets.Set[string], 0),
158 }}
159
160 for tci, tc := range testCases {
161
162 localIPs := tc.endpointsMap.getLocalReadyEndpointIPs()
163
164 if !reflect.DeepEqual(localIPs, tc.expected) {
165 t.Errorf("[%d] expected %#v, got %#v", tci, tc.expected, localIPs)
166 }
167 }
168 }
169
170 func makeTestEndpointSlice(namespace, name string, slice int, epsFunc func(*discovery.EndpointSlice)) *discovery.EndpointSlice {
171 eps := &discovery.EndpointSlice{
172 ObjectMeta: metav1.ObjectMeta{
173 Name: fmt.Sprintf("%s-%d", name, slice),
174 Namespace: namespace,
175 Annotations: map[string]string{},
176 Labels: map[string]string{
177 discovery.LabelServiceName: name,
178 },
179 },
180 AddressType: discovery.AddressTypeIPv4,
181 }
182 epsFunc(eps)
183 return eps
184 }
185
186 func TestUpdateEndpointsMap(t *testing.T) {
187 emptyEndpoint := func(eps *discovery.EndpointSlice) {
188 eps.Endpoints = []discovery.Endpoint{}
189 }
190 unnamedPort := func(eps *discovery.EndpointSlice) {
191 eps.Endpoints = []discovery.Endpoint{{
192 Addresses: []string{"1.1.1.1"},
193 }}
194 eps.Ports = []discovery.EndpointPort{{
195 Name: ptr.To(""),
196 Port: ptr.To[int32](11),
197 Protocol: ptr.To(v1.ProtocolUDP),
198 }}
199 }
200 unnamedPortReady := func(eps *discovery.EndpointSlice) {
201 eps.Endpoints = []discovery.Endpoint{{
202 Addresses: []string{"1.1.1.1"},
203 Conditions: discovery.EndpointConditions{
204 Ready: ptr.To(true),
205 Serving: ptr.To(true),
206 Terminating: ptr.To(false),
207 },
208 }}
209 eps.Ports = []discovery.EndpointPort{{
210 Name: ptr.To(""),
211 Port: ptr.To[int32](11),
212 Protocol: ptr.To(v1.ProtocolUDP),
213 }}
214 }
215 unnamedPortTerminating := func(eps *discovery.EndpointSlice) {
216 eps.Endpoints = []discovery.Endpoint{{
217 Addresses: []string{"1.1.1.1"},
218 Conditions: discovery.EndpointConditions{
219 Ready: ptr.To(false),
220 Serving: ptr.To(true),
221 Terminating: ptr.To(true),
222 },
223 }}
224 eps.Ports = []discovery.EndpointPort{{
225 Name: ptr.To(""),
226 Port: ptr.To[int32](11),
227 Protocol: ptr.To(v1.ProtocolUDP),
228 }}
229 }
230 unnamedPortLocal := func(eps *discovery.EndpointSlice) {
231 eps.Endpoints = []discovery.Endpoint{{
232 Addresses: []string{"1.1.1.1"},
233 NodeName: ptr.To(testHostname),
234 }}
235 eps.Ports = []discovery.EndpointPort{{
236 Name: ptr.To(""),
237 Port: ptr.To[int32](11),
238 Protocol: ptr.To(v1.ProtocolUDP),
239 }}
240 }
241 namedPortLocal := func(eps *discovery.EndpointSlice) {
242 eps.Endpoints = []discovery.Endpoint{{
243 Addresses: []string{"1.1.1.1"},
244 NodeName: ptr.To(testHostname),
245 }}
246 eps.Ports = []discovery.EndpointPort{{
247 Name: ptr.To("p11"),
248 Port: ptr.To[int32](11),
249 Protocol: ptr.To(v1.ProtocolUDP),
250 }}
251 }
252 namedPort := func(eps *discovery.EndpointSlice) {
253 eps.Endpoints = []discovery.Endpoint{{
254 Addresses: []string{"1.1.1.1"},
255 }}
256 eps.Ports = []discovery.EndpointPort{{
257 Name: ptr.To("p11"),
258 Port: ptr.To[int32](11),
259 Protocol: ptr.To(v1.ProtocolUDP),
260 }}
261 }
262 namedPortRenamed := func(eps *discovery.EndpointSlice) {
263 eps.Endpoints = []discovery.Endpoint{{
264 Addresses: []string{"1.1.1.1"},
265 }}
266 eps.Ports = []discovery.EndpointPort{{
267 Name: ptr.To("p11-2"),
268 Port: ptr.To[int32](11),
269 Protocol: ptr.To(v1.ProtocolUDP),
270 }}
271 }
272 namedPortRenumbered := func(eps *discovery.EndpointSlice) {
273 eps.Endpoints = []discovery.Endpoint{{
274 Addresses: []string{"1.1.1.1"},
275 }}
276 eps.Ports = []discovery.EndpointPort{{
277 Name: ptr.To("p11"),
278 Port: ptr.To[int32](22),
279 Protocol: ptr.To(v1.ProtocolUDP),
280 }}
281 }
282 namedPortsLocalNoLocal := func(eps *discovery.EndpointSlice) {
283 eps.Endpoints = []discovery.Endpoint{{
284 Addresses: []string{"1.1.1.1"},
285 }, {
286 Addresses: []string{"1.1.1.2"},
287 NodeName: ptr.To(testHostname),
288 }}
289 eps.Ports = []discovery.EndpointPort{{
290 Name: ptr.To("p11"),
291 Port: ptr.To[int32](11),
292 Protocol: ptr.To(v1.ProtocolUDP),
293 }, {
294 Name: ptr.To("p12"),
295 Port: ptr.To[int32](12),
296 Protocol: ptr.To(v1.ProtocolUDP),
297 }}
298 }
299 multipleSubsets_s1 := func(eps *discovery.EndpointSlice) {
300 eps.Endpoints = []discovery.Endpoint{{
301 Addresses: []string{"1.1.1.1"},
302 }}
303 eps.Ports = []discovery.EndpointPort{{
304 Name: ptr.To("p11"),
305 Port: ptr.To[int32](11),
306 Protocol: ptr.To(v1.ProtocolUDP),
307 }}
308 }
309 multipleSubsets_s2 := func(eps *discovery.EndpointSlice) {
310 eps.Endpoints = []discovery.Endpoint{{
311 Addresses: []string{"1.1.1.2"},
312 }}
313 eps.Ports = []discovery.EndpointPort{{
314 Name: ptr.To("p12"),
315 Port: ptr.To[int32](12),
316 Protocol: ptr.To(v1.ProtocolUDP),
317 }}
318 }
319 multipleSubsetsWithLocal_s1 := func(eps *discovery.EndpointSlice) {
320 eps.Endpoints = []discovery.Endpoint{{
321 Addresses: []string{"1.1.1.1"},
322 }}
323 eps.Ports = []discovery.EndpointPort{{
324 Name: ptr.To("p11"),
325 Port: ptr.To[int32](11),
326 Protocol: ptr.To(v1.ProtocolUDP),
327 }}
328 }
329 multipleSubsetsWithLocal_s2 := func(eps *discovery.EndpointSlice) {
330 eps.Endpoints = []discovery.Endpoint{{
331 Addresses: []string{"1.1.1.2"},
332 NodeName: ptr.To(testHostname),
333 }}
334 eps.Ports = []discovery.EndpointPort{{
335 Name: ptr.To("p12"),
336 Port: ptr.To[int32](12),
337 Protocol: ptr.To(v1.ProtocolUDP),
338 }}
339 }
340 multipleSubsetsMultiplePortsLocal_s1 := func(eps *discovery.EndpointSlice) {
341 eps.Endpoints = []discovery.Endpoint{{
342 Addresses: []string{"1.1.1.1"},
343 NodeName: ptr.To(testHostname),
344 }}
345 eps.Ports = []discovery.EndpointPort{{
346 Name: ptr.To("p11"),
347 Port: ptr.To[int32](11),
348 Protocol: ptr.To(v1.ProtocolUDP),
349 }, {
350 Name: ptr.To("p12"),
351 Port: ptr.To[int32](12),
352 Protocol: ptr.To(v1.ProtocolUDP),
353 }}
354 }
355 multipleSubsetsMultiplePortsLocal_s2 := func(eps *discovery.EndpointSlice) {
356 eps.Endpoints = []discovery.Endpoint{{
357 Addresses: []string{"1.1.1.3"},
358 }}
359 eps.Ports = []discovery.EndpointPort{{
360 Name: ptr.To("p13"),
361 Port: ptr.To[int32](13),
362 Protocol: ptr.To(v1.ProtocolUDP),
363 }}
364 }
365 multipleSubsetsIPsPorts1_s1 := func(eps *discovery.EndpointSlice) {
366 eps.Endpoints = []discovery.Endpoint{{
367 Addresses: []string{"1.1.1.1"},
368 }, {
369 Addresses: []string{"1.1.1.2"},
370 NodeName: ptr.To(testHostname),
371 }}
372 eps.Ports = []discovery.EndpointPort{{
373 Name: ptr.To("p11"),
374 Port: ptr.To[int32](11),
375 Protocol: ptr.To(v1.ProtocolUDP),
376 }, {
377 Name: ptr.To("p12"),
378 Port: ptr.To[int32](12),
379 Protocol: ptr.To(v1.ProtocolUDP),
380 }}
381 }
382 multipleSubsetsIPsPorts1_s2 := func(eps *discovery.EndpointSlice) {
383 eps.Endpoints = []discovery.Endpoint{{
384 Addresses: []string{"1.1.1.3"},
385 }, {
386 Addresses: []string{"1.1.1.4"},
387 NodeName: ptr.To(testHostname),
388 }}
389 eps.Ports = []discovery.EndpointPort{{
390 Name: ptr.To("p13"),
391 Port: ptr.To[int32](13),
392 Protocol: ptr.To(v1.ProtocolUDP),
393 }, {
394 Name: ptr.To("p14"),
395 Port: ptr.To[int32](14),
396 Protocol: ptr.To(v1.ProtocolUDP),
397 }}
398 }
399 multipleSubsetsIPsPorts2 := func(eps *discovery.EndpointSlice) {
400 eps.Endpoints = []discovery.Endpoint{{
401 Addresses: []string{"2.2.2.1"},
402 }, {
403 Addresses: []string{"2.2.2.2"},
404 NodeName: ptr.To(testHostname),
405 }}
406 eps.Ports = []discovery.EndpointPort{{
407 Name: ptr.To("p21"),
408 Port: ptr.To[int32](21),
409 Protocol: ptr.To(v1.ProtocolUDP),
410 }, {
411 Name: ptr.To("p22"),
412 Port: ptr.To[int32](22),
413 Protocol: ptr.To(v1.ProtocolUDP),
414 }}
415 }
416 complexBefore1 := func(eps *discovery.EndpointSlice) {
417 eps.Endpoints = []discovery.Endpoint{{
418 Addresses: []string{"1.1.1.1"},
419 }}
420 eps.Ports = []discovery.EndpointPort{{
421 Name: ptr.To("p11"),
422 Port: ptr.To[int32](11),
423 Protocol: ptr.To(v1.ProtocolUDP),
424 }}
425 }
426 complexBefore2_s1 := func(eps *discovery.EndpointSlice) {
427 eps.Endpoints = []discovery.Endpoint{{
428 Addresses: []string{"2.2.2.2"},
429 NodeName: ptr.To(testHostname),
430 }, {
431 Addresses: []string{"2.2.2.22"},
432 NodeName: ptr.To(testHostname),
433 }}
434 eps.Ports = []discovery.EndpointPort{{
435 Name: ptr.To("p22"),
436 Port: ptr.To[int32](22),
437 Protocol: ptr.To(v1.ProtocolUDP),
438 }}
439 }
440 complexBefore2_s2 := func(eps *discovery.EndpointSlice) {
441 eps.Endpoints = []discovery.Endpoint{{
442 Addresses: []string{"2.2.2.3"},
443 NodeName: ptr.To(testHostname),
444 }}
445 eps.Ports = []discovery.EndpointPort{{
446 Name: ptr.To("p23"),
447 Port: ptr.To[int32](23),
448 Protocol: ptr.To(v1.ProtocolUDP),
449 }}
450 }
451 complexBefore4_s1 := func(eps *discovery.EndpointSlice) {
452 eps.Endpoints = []discovery.Endpoint{{
453 Addresses: []string{"4.4.4.4"},
454 NodeName: ptr.To(testHostname),
455 }, {
456 Addresses: []string{"4.4.4.5"},
457 NodeName: ptr.To(testHostname),
458 }}
459 eps.Ports = []discovery.EndpointPort{{
460 Name: ptr.To("p44"),
461 Port: ptr.To[int32](44),
462 Protocol: ptr.To(v1.ProtocolUDP),
463 }}
464 }
465 complexBefore4_s2 := func(eps *discovery.EndpointSlice) {
466 eps.Endpoints = []discovery.Endpoint{{
467 Addresses: []string{"4.4.4.6"},
468 NodeName: ptr.To(testHostname),
469 }}
470 eps.Ports = []discovery.EndpointPort{{
471 Name: ptr.To("p45"),
472 Port: ptr.To[int32](45),
473 Protocol: ptr.To(v1.ProtocolUDP),
474 }}
475 }
476 complexAfter1_s1 := func(eps *discovery.EndpointSlice) {
477 eps.Endpoints = []discovery.Endpoint{{
478 Addresses: []string{"1.1.1.1"},
479 }, {
480 Addresses: []string{"1.1.1.11"},
481 }}
482 eps.Ports = []discovery.EndpointPort{{
483 Name: ptr.To("p11"),
484 Port: ptr.To[int32](11),
485 Protocol: ptr.To(v1.ProtocolUDP),
486 }}
487 }
488 complexAfter1_s2 := func(eps *discovery.EndpointSlice) {
489 eps.Endpoints = []discovery.Endpoint{{
490 Addresses: []string{"1.1.1.2"},
491 }}
492 eps.Ports = []discovery.EndpointPort{{
493 Name: ptr.To("p12"),
494 Port: ptr.To[int32](12),
495 Protocol: ptr.To(v1.ProtocolUDP),
496 }, {
497 Name: ptr.To("p122"),
498 Port: ptr.To[int32](122),
499 Protocol: ptr.To(v1.ProtocolUDP),
500 }}
501 }
502 complexAfter3 := func(eps *discovery.EndpointSlice) {
503 eps.Endpoints = []discovery.Endpoint{{
504 Addresses: []string{"3.3.3.3"},
505 }}
506 eps.Ports = []discovery.EndpointPort{{
507 Name: ptr.To("p33"),
508 Port: ptr.To[int32](33),
509 Protocol: ptr.To(v1.ProtocolUDP),
510 }}
511 }
512 complexAfter4 := func(eps *discovery.EndpointSlice) {
513 eps.Endpoints = []discovery.Endpoint{{
514 Addresses: []string{"4.4.4.4"},
515 NodeName: ptr.To(testHostname),
516 }}
517 eps.Ports = []discovery.EndpointPort{{
518 Name: ptr.To("p44"),
519 Port: ptr.To[int32](44),
520 Protocol: ptr.To(v1.ProtocolUDP),
521 }}
522 }
523
524 testCases := []struct {
525
526
527
528 name string
529 previousEndpointSlices []*discovery.EndpointSlice
530 currentEndpointSlices []*discovery.EndpointSlice
531 previousEndpointsMap map[ServicePortName][]*BaseEndpointInfo
532 expectedResult map[ServicePortName][]*BaseEndpointInfo
533 expectedDeletedUDPEndpoints []ServiceEndpoint
534 expectedNewlyActiveUDPServices map[ServicePortName]bool
535 expectedLocalEndpoints map[types.NamespacedName]int
536 expectedChangedEndpoints sets.Set[types.NamespacedName]
537 }{{
538 name: "empty",
539 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{},
540 expectedResult: map[ServicePortName][]*BaseEndpointInfo{},
541 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
542 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
543 expectedLocalEndpoints: map[types.NamespacedName]int{},
544 expectedChangedEndpoints: sets.New[types.NamespacedName](),
545 }, {
546 name: "no change, unnamed port",
547 previousEndpointSlices: []*discovery.EndpointSlice{
548 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPort),
549 },
550 currentEndpointSlices: []*discovery.EndpointSlice{
551 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPort),
552 },
553 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
554 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
555 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
556 },
557 },
558 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
559 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
560 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
561 },
562 },
563 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
564 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
565 expectedLocalEndpoints: map[types.NamespacedName]int{},
566 expectedChangedEndpoints: sets.New[types.NamespacedName](),
567 }, {
568 name: "no change, named port, local",
569 previousEndpointSlices: []*discovery.EndpointSlice{
570 makeTestEndpointSlice("ns1", "ep1", 1, namedPortLocal),
571 },
572 currentEndpointSlices: []*discovery.EndpointSlice{
573 makeTestEndpointSlice("ns1", "ep1", 1, namedPortLocal),
574 },
575 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
576 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
577 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
578 },
579 },
580 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
581 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
582 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
583 },
584 },
585 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
586 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
587 expectedLocalEndpoints: map[types.NamespacedName]int{
588 makeNSN("ns1", "ep1"): 1,
589 },
590 expectedChangedEndpoints: sets.New[types.NamespacedName](),
591 }, {
592 name: "no change, multiple slices",
593 previousEndpointSlices: []*discovery.EndpointSlice{
594 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsets_s1),
595 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsets_s2),
596 },
597 currentEndpointSlices: []*discovery.EndpointSlice{
598 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsets_s1),
599 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsets_s2),
600 },
601 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
602 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
603 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
604 },
605 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
606 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: false, ready: true, serving: true, terminating: false},
607 },
608 },
609 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
610 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
611 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
612 },
613 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
614 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: false, ready: true, serving: true, terminating: false},
615 },
616 },
617 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
618 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
619 expectedLocalEndpoints: map[types.NamespacedName]int{},
620 expectedChangedEndpoints: sets.New[types.NamespacedName](),
621 }, {
622 name: "no change, multiple slices, multiple ports, local",
623 previousEndpointSlices: []*discovery.EndpointSlice{
624 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsetsMultiplePortsLocal_s1),
625 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsetsMultiplePortsLocal_s2),
626 },
627 currentEndpointSlices: []*discovery.EndpointSlice{
628 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsetsMultiplePortsLocal_s1),
629 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsetsMultiplePortsLocal_s2),
630 },
631 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
632 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
633 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
634 },
635 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
636 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: true, ready: true, serving: true, terminating: false},
637 },
638 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
639 {ip: "1.1.1.3", port: 13, endpoint: "1.1.1.3:13", isLocal: false, ready: true, serving: true, terminating: false},
640 },
641 },
642 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
643 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
644 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
645 },
646 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
647 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: true, ready: true, serving: true, terminating: false},
648 },
649 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
650 {ip: "1.1.1.3", port: 13, endpoint: "1.1.1.3:13", isLocal: false, ready: true, serving: true, terminating: false},
651 },
652 },
653 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
654 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
655 expectedLocalEndpoints: map[types.NamespacedName]int{
656 makeNSN("ns1", "ep1"): 1,
657 },
658 expectedChangedEndpoints: sets.New[types.NamespacedName](),
659 }, {
660 name: "no change, multiple services, slices, IPs, and ports",
661 previousEndpointSlices: []*discovery.EndpointSlice{
662 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsetsIPsPorts1_s1),
663 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsetsIPsPorts1_s2),
664 makeTestEndpointSlice("ns2", "ep2", 1, multipleSubsetsIPsPorts2),
665 },
666 currentEndpointSlices: []*discovery.EndpointSlice{
667 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsetsIPsPorts1_s1),
668 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsetsIPsPorts1_s2),
669 makeTestEndpointSlice("ns2", "ep2", 1, multipleSubsetsIPsPorts2),
670 },
671 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
672 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
673 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
674 {ip: "1.1.1.2", port: 11, endpoint: "1.1.1.2:11", isLocal: true, ready: true, serving: true, terminating: false},
675 },
676 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
677 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: false, ready: true, serving: true, terminating: false},
678 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
679 },
680 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
681 {ip: "1.1.1.3", port: 13, endpoint: "1.1.1.3:13", isLocal: false, ready: true, serving: true, terminating: false},
682 {ip: "1.1.1.4", port: 13, endpoint: "1.1.1.4:13", isLocal: true, ready: true, serving: true, terminating: false},
683 },
684 makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): {
685 {ip: "1.1.1.3", port: 14, endpoint: "1.1.1.3:14", isLocal: false, ready: true, serving: true, terminating: false},
686 {ip: "1.1.1.4", port: 14, endpoint: "1.1.1.4:14", isLocal: true, ready: true, serving: true, terminating: false},
687 },
688 makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): {
689 {ip: "2.2.2.1", port: 21, endpoint: "2.2.2.1:21", isLocal: false, ready: true, serving: true, terminating: false},
690 {ip: "2.2.2.2", port: 21, endpoint: "2.2.2.2:21", isLocal: true, ready: true, serving: true, terminating: false},
691 },
692 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
693 {ip: "2.2.2.1", port: 22, endpoint: "2.2.2.1:22", isLocal: false, ready: true, serving: true, terminating: false},
694 {ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: true, serving: true, terminating: false},
695 },
696 },
697 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
698 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
699 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
700 {ip: "1.1.1.2", port: 11, endpoint: "1.1.1.2:11", isLocal: true, ready: true, serving: true, terminating: false},
701 },
702 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
703 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: false, ready: true, serving: true, terminating: false},
704 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
705 },
706 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): {
707 {ip: "1.1.1.3", port: 13, endpoint: "1.1.1.3:13", isLocal: false, ready: true, serving: true, terminating: false},
708 {ip: "1.1.1.4", port: 13, endpoint: "1.1.1.4:13", isLocal: true, ready: true, serving: true, terminating: false},
709 },
710 makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): {
711 {ip: "1.1.1.3", port: 14, endpoint: "1.1.1.3:14", isLocal: false, ready: true, serving: true, terminating: false},
712 {ip: "1.1.1.4", port: 14, endpoint: "1.1.1.4:14", isLocal: true, ready: true, serving: true, terminating: false},
713 },
714 makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): {
715 {ip: "2.2.2.1", port: 21, endpoint: "2.2.2.1:21", isLocal: false, ready: true, serving: true, terminating: false},
716 {ip: "2.2.2.2", port: 21, endpoint: "2.2.2.2:21", isLocal: true, ready: true, serving: true, terminating: false},
717 },
718 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
719 {ip: "2.2.2.1", port: 22, endpoint: "2.2.2.1:22", isLocal: false, ready: true, serving: true, terminating: false},
720 {ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: true, serving: true, terminating: false},
721 },
722 },
723 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
724 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
725 expectedLocalEndpoints: map[types.NamespacedName]int{
726 makeNSN("ns1", "ep1"): 2,
727 makeNSN("ns2", "ep2"): 1,
728 },
729 expectedChangedEndpoints: sets.New[types.NamespacedName](),
730 }, {
731 name: "add an EndpointSlice",
732 previousEndpointSlices: []*discovery.EndpointSlice{
733 nil,
734 },
735 currentEndpointSlices: []*discovery.EndpointSlice{
736 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPortLocal),
737 },
738 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{},
739 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
740 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
741 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
742 },
743 },
744 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
745 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
746 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): true,
747 },
748 expectedLocalEndpoints: map[types.NamespacedName]int{
749 makeNSN("ns1", "ep1"): 1,
750 },
751 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
752 }, {
753 name: "remove an EndpointSlice",
754 previousEndpointSlices: []*discovery.EndpointSlice{
755 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPortLocal),
756 },
757 currentEndpointSlices: []*discovery.EndpointSlice{
758 nil,
759 },
760 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
761 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
762 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: true, ready: true, serving: true, terminating: false},
763 },
764 },
765 expectedResult: map[ServicePortName][]*BaseEndpointInfo{},
766 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
767 Endpoint: "1.1.1.1:11",
768 ServicePortName: makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP),
769 }},
770 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
771 expectedLocalEndpoints: map[types.NamespacedName]int{},
772 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
773 }, {
774 name: "add an IP and port",
775 previousEndpointSlices: []*discovery.EndpointSlice{
776 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
777 },
778 currentEndpointSlices: []*discovery.EndpointSlice{
779 makeTestEndpointSlice("ns1", "ep1", 1, namedPortsLocalNoLocal),
780 },
781 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
782 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
783 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
784 },
785 },
786 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
787 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
788 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
789 {ip: "1.1.1.2", port: 11, endpoint: "1.1.1.2:11", isLocal: true, ready: true, serving: true, terminating: false},
790 },
791 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
792 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: false, ready: true, serving: true, terminating: false},
793 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
794 },
795 },
796 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
797 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
798 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true,
799 },
800 expectedLocalEndpoints: map[types.NamespacedName]int{
801 makeNSN("ns1", "ep1"): 1,
802 },
803 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
804 }, {
805 name: "remove an IP and port",
806 previousEndpointSlices: []*discovery.EndpointSlice{
807 makeTestEndpointSlice("ns1", "ep1", 1, namedPortsLocalNoLocal),
808 },
809 currentEndpointSlices: []*discovery.EndpointSlice{
810 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
811 },
812 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
813 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
814 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
815 {ip: "1.1.1.2", port: 11, endpoint: "1.1.1.2:11", isLocal: true, ready: true, serving: true, terminating: false},
816 },
817 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
818 {ip: "1.1.1.1", port: 12, endpoint: "1.1.1.1:12", isLocal: false, ready: true, serving: true, terminating: false},
819 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
820 },
821 },
822 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
823 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
824 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
825 },
826 },
827 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
828 Endpoint: "1.1.1.2:11",
829 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
830 }, {
831 Endpoint: "1.1.1.1:12",
832 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
833 }, {
834 Endpoint: "1.1.1.2:12",
835 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
836 }},
837 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
838 expectedLocalEndpoints: map[types.NamespacedName]int{},
839 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
840 }, {
841 name: "add a slice to an endpoint",
842 previousEndpointSlices: []*discovery.EndpointSlice{
843 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
844 nil,
845 },
846 currentEndpointSlices: []*discovery.EndpointSlice{
847 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsetsWithLocal_s1),
848 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsetsWithLocal_s2),
849 },
850 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
851 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
852 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
853 },
854 },
855 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
856 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
857 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
858 },
859 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
860 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: true, ready: true, serving: true, terminating: false},
861 },
862 },
863 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
864 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
865 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true,
866 },
867 expectedLocalEndpoints: map[types.NamespacedName]int{
868 makeNSN("ns1", "ep1"): 1,
869 },
870 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
871 }, {
872 name: "remove a slice from an endpoint",
873 previousEndpointSlices: []*discovery.EndpointSlice{
874 makeTestEndpointSlice("ns1", "ep1", 1, multipleSubsets_s1),
875 makeTestEndpointSlice("ns1", "ep1", 2, multipleSubsets_s2),
876 },
877 currentEndpointSlices: []*discovery.EndpointSlice{
878 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
879 nil,
880 },
881 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
882 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
883 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
884 },
885 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
886 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: false, ready: true, serving: true, terminating: false},
887 },
888 },
889 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
890 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
891 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
892 },
893 },
894 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
895 Endpoint: "1.1.1.2:12",
896 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP),
897 }},
898 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
899 expectedLocalEndpoints: map[types.NamespacedName]int{},
900 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
901 }, {
902 name: "rename a port",
903 previousEndpointSlices: []*discovery.EndpointSlice{
904 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
905 },
906 currentEndpointSlices: []*discovery.EndpointSlice{
907 makeTestEndpointSlice("ns1", "ep1", 1, namedPortRenamed),
908 },
909 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
910 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
911 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
912 },
913 },
914 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
915 makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): {
916 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
917 },
918 },
919 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
920 Endpoint: "1.1.1.1:11",
921 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
922 }},
923 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
924 makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): true,
925 },
926 expectedLocalEndpoints: map[types.NamespacedName]int{},
927 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
928 }, {
929 name: "renumber a port",
930 previousEndpointSlices: []*discovery.EndpointSlice{
931 makeTestEndpointSlice("ns1", "ep1", 1, namedPort),
932 },
933 currentEndpointSlices: []*discovery.EndpointSlice{
934 makeTestEndpointSlice("ns1", "ep1", 1, namedPortRenumbered),
935 },
936 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
937 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
938 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
939 },
940 },
941 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
942 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
943 {ip: "1.1.1.1", port: 22, endpoint: "1.1.1.1:22", isLocal: false, ready: true, serving: true, terminating: false},
944 },
945 },
946 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
947 Endpoint: "1.1.1.1:11",
948 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP),
949 }},
950 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
951 expectedLocalEndpoints: map[types.NamespacedName]int{},
952 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
953 }, {
954 name: "complex add and remove",
955 previousEndpointSlices: []*discovery.EndpointSlice{
956 makeTestEndpointSlice("ns1", "ep1", 1, complexBefore1),
957 nil,
958
959 makeTestEndpointSlice("ns2", "ep2", 1, complexBefore2_s1),
960 makeTestEndpointSlice("ns2", "ep2", 2, complexBefore2_s2),
961
962 nil,
963 nil,
964
965 makeTestEndpointSlice("ns4", "ep4", 1, complexBefore4_s1),
966 makeTestEndpointSlice("ns4", "ep4", 2, complexBefore4_s2),
967 },
968 currentEndpointSlices: []*discovery.EndpointSlice{
969 makeTestEndpointSlice("ns1", "ep1", 1, complexAfter1_s1),
970 makeTestEndpointSlice("ns1", "ep1", 2, complexAfter1_s2),
971
972 nil,
973 nil,
974
975 makeTestEndpointSlice("ns3", "ep3", 1, complexAfter3),
976 nil,
977
978 makeTestEndpointSlice("ns4", "ep4", 1, complexAfter4),
979 nil,
980 },
981 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
982 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
983 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
984 },
985 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): {
986 {ip: "2.2.2.22", port: 22, endpoint: "2.2.2.22:22", isLocal: true, ready: true, serving: true, terminating: false},
987 {ip: "2.2.2.2", port: 22, endpoint: "2.2.2.2:22", isLocal: true, ready: true, serving: true, terminating: false},
988 },
989 makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP): {
990 {ip: "2.2.2.3", port: 23, endpoint: "2.2.2.3:23", isLocal: true, ready: true, serving: true, terminating: false},
991 },
992 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): {
993 {ip: "4.4.4.4", port: 44, endpoint: "4.4.4.4:44", isLocal: true, ready: true, serving: true, terminating: false},
994 {ip: "4.4.4.5", port: 44, endpoint: "4.4.4.5:44", isLocal: true, ready: true, serving: true, terminating: false},
995 },
996 makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP): {
997 {ip: "4.4.4.6", port: 45, endpoint: "4.4.4.6:45", isLocal: true, ready: true, serving: true, terminating: false},
998 },
999 },
1000 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
1001 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): {
1002 {ip: "1.1.1.11", port: 11, endpoint: "1.1.1.11:11", isLocal: false, ready: true, serving: true, terminating: false},
1003 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
1004 },
1005 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): {
1006 {ip: "1.1.1.2", port: 12, endpoint: "1.1.1.2:12", isLocal: false, ready: true, serving: true, terminating: false},
1007 },
1008 makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): {
1009 {ip: "1.1.1.2", port: 122, endpoint: "1.1.1.2:122", isLocal: false, ready: true, serving: true, terminating: false},
1010 },
1011 makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP): {
1012 {ip: "3.3.3.3", port: 33, endpoint: "3.3.3.3:33", isLocal: false, ready: true, serving: true, terminating: false},
1013 },
1014 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): {
1015 {ip: "4.4.4.4", port: 44, endpoint: "4.4.4.4:44", isLocal: true, ready: true, serving: true, terminating: false},
1016 },
1017 },
1018 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
1019 Endpoint: "2.2.2.2:22",
1020 ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP),
1021 }, {
1022 Endpoint: "2.2.2.22:22",
1023 ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP),
1024 }, {
1025 Endpoint: "2.2.2.3:23",
1026 ServicePortName: makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP),
1027 }, {
1028 Endpoint: "4.4.4.5:44",
1029 ServicePortName: makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP),
1030 }, {
1031 Endpoint: "4.4.4.6:45",
1032 ServicePortName: makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP),
1033 }},
1034 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
1035 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true,
1036 makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): true,
1037 makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP): true,
1038 },
1039 expectedLocalEndpoints: map[types.NamespacedName]int{
1040 makeNSN("ns4", "ep4"): 1,
1041 },
1042 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1"), makeNSN("ns2", "ep2"), makeNSN("ns3", "ep3"), makeNSN("ns4", "ep4")),
1043 }, {
1044 name: "change from 0 endpoint address to 1 unnamed port",
1045 previousEndpointSlices: []*discovery.EndpointSlice{
1046 makeTestEndpointSlice("ns1", "ep1", 1, emptyEndpoint),
1047 },
1048 currentEndpointSlices: []*discovery.EndpointSlice{
1049 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPort),
1050 },
1051 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{},
1052 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
1053 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
1054 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
1055 },
1056 },
1057 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
1058 expectedNewlyActiveUDPServices: map[ServicePortName]bool{
1059 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): true,
1060 },
1061 expectedLocalEndpoints: map[types.NamespacedName]int{},
1062 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
1063 }, {
1064 name: "change from ready to terminating pod",
1065 previousEndpointSlices: []*discovery.EndpointSlice{
1066 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPortReady),
1067 },
1068 currentEndpointSlices: []*discovery.EndpointSlice{
1069 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPortTerminating),
1070 },
1071 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
1072 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
1073 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: true, serving: true, terminating: false},
1074 },
1075 },
1076 expectedResult: map[ServicePortName][]*BaseEndpointInfo{
1077 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
1078 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: false, serving: true, terminating: true},
1079 },
1080 },
1081 expectedDeletedUDPEndpoints: []ServiceEndpoint{},
1082 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
1083 expectedLocalEndpoints: map[types.NamespacedName]int{},
1084 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
1085 }, {
1086 name: "change from terminating to empty pod",
1087 previousEndpointSlices: []*discovery.EndpointSlice{
1088 makeTestEndpointSlice("ns1", "ep1", 1, unnamedPortTerminating),
1089 },
1090 currentEndpointSlices: []*discovery.EndpointSlice{
1091 makeTestEndpointSlice("ns1", "ep1", 1, emptyEndpoint),
1092 },
1093 previousEndpointsMap: map[ServicePortName][]*BaseEndpointInfo{
1094 makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP): {
1095 {ip: "1.1.1.1", port: 11, endpoint: "1.1.1.1:11", isLocal: false, ready: false, serving: true, terminating: true},
1096 },
1097 },
1098 expectedResult: map[ServicePortName][]*BaseEndpointInfo{},
1099 expectedDeletedUDPEndpoints: []ServiceEndpoint{{
1100 Endpoint: "1.1.1.1:11",
1101 ServicePortName: makeServicePortName("ns1", "ep1", "", v1.ProtocolUDP),
1102 }},
1103 expectedNewlyActiveUDPServices: map[ServicePortName]bool{},
1104 expectedLocalEndpoints: map[types.NamespacedName]int{},
1105 expectedChangedEndpoints: sets.New(makeNSN("ns1", "ep1")),
1106 },
1107 }
1108
1109 for tci, tc := range testCases {
1110 t.Run(tc.name, func(t *testing.T) {
1111 fp := newFakeProxier(v1.IPv4Protocol, time.Time{})
1112 fp.hostname = testHostname
1113
1114
1115
1116 for i := range tc.previousEndpointSlices {
1117 if tc.previousEndpointSlices[i] != nil {
1118 fp.addEndpointSlice(tc.previousEndpointSlices[i])
1119 }
1120 }
1121 fp.endpointsMap.Update(fp.endpointsChanges)
1122 compareEndpointsMapsStr(t, fp.endpointsMap, tc.previousEndpointsMap)
1123
1124
1125 if len(tc.previousEndpointSlices) != len(tc.currentEndpointSlices) {
1126 t.Fatalf("[%d] different lengths of previous and current endpoints", tci)
1127 return
1128 }
1129
1130 for i := range tc.previousEndpointSlices {
1131 prev, curr := tc.previousEndpointSlices[i], tc.currentEndpointSlices[i]
1132 switch {
1133 case prev == nil && curr == nil:
1134 continue
1135 case prev == nil:
1136 fp.addEndpointSlice(curr)
1137 case curr == nil:
1138 fp.deleteEndpointSlice(prev)
1139 default:
1140 fp.updateEndpointSlice(prev, curr)
1141 }
1142 }
1143
1144 result := fp.endpointsMap.Update(fp.endpointsChanges)
1145 newMap := fp.endpointsMap
1146 compareEndpointsMapsStr(t, newMap, tc.expectedResult)
1147 if !result.UpdatedServices.Equal(tc.expectedChangedEndpoints) {
1148 t.Errorf("[%d] expected changed endpoints %q, got %q", tci, tc.expectedChangedEndpoints.UnsortedList(), result.UpdatedServices.UnsortedList())
1149 }
1150 if len(result.DeletedUDPEndpoints) != len(tc.expectedDeletedUDPEndpoints) {
1151 t.Errorf("[%d] expected %d staleEndpoints, got %d: %v", tci, len(tc.expectedDeletedUDPEndpoints), len(result.DeletedUDPEndpoints), result.DeletedUDPEndpoints)
1152 }
1153 for _, x := range tc.expectedDeletedUDPEndpoints {
1154 found := false
1155 for _, stale := range result.DeletedUDPEndpoints {
1156 if stale == x {
1157 found = true
1158 break
1159 }
1160 }
1161 if !found {
1162 t.Errorf("[%d] expected staleEndpoints[%v], but didn't find it: %v", tci, x, result.DeletedUDPEndpoints)
1163 }
1164 }
1165 if len(result.NewlyActiveUDPServices) != len(tc.expectedNewlyActiveUDPServices) {
1166 t.Errorf("[%d] expected %d newlyActiveUDPServices, got %d: %v", tci, len(tc.expectedNewlyActiveUDPServices), len(result.NewlyActiveUDPServices), result.NewlyActiveUDPServices)
1167 }
1168 for svcName := range tc.expectedNewlyActiveUDPServices {
1169 found := false
1170 for _, newSvcName := range result.NewlyActiveUDPServices {
1171 if newSvcName == svcName {
1172 found = true
1173 }
1174 }
1175 if !found {
1176 t.Errorf("[%d] expected newlyActiveUDPServices[%v], but didn't find it: %v", tci, svcName, result.NewlyActiveUDPServices)
1177 }
1178 }
1179
1180 localReadyEndpoints := fp.endpointsMap.LocalReadyEndpoints()
1181 if !reflect.DeepEqual(localReadyEndpoints, tc.expectedLocalEndpoints) {
1182 t.Errorf("[%d] expected local ready endpoints %v, got %v", tci, tc.expectedLocalEndpoints, localReadyEndpoints)
1183 }
1184 })
1185 }
1186 }
1187
1188 func TestLastChangeTriggerTime(t *testing.T) {
1189 startTime := time.Date(2018, 01, 01, 0, 0, 0, 0, time.UTC)
1190 t_1 := startTime.Add(-time.Second)
1191 t0 := startTime.Add(time.Second)
1192 t1 := t0.Add(time.Second)
1193 t2 := t1.Add(time.Second)
1194 t3 := t2.Add(time.Second)
1195
1196 createEndpoints := func(namespace, name string, triggerTime time.Time) *discovery.EndpointSlice {
1197 return &discovery.EndpointSlice{
1198 ObjectMeta: metav1.ObjectMeta{
1199 Name: name,
1200 Namespace: namespace,
1201 Annotations: map[string]string{
1202 v1.EndpointsLastChangeTriggerTime: triggerTime.Format(time.RFC3339Nano),
1203 },
1204 Labels: map[string]string{
1205 discovery.LabelServiceName: name,
1206 },
1207 },
1208 AddressType: discovery.AddressTypeIPv4,
1209 Endpoints: []discovery.Endpoint{{
1210 Addresses: []string{"1.1.1.1"},
1211 }},
1212 Ports: []discovery.EndpointPort{{
1213 Name: ptr.To("p11"),
1214 Port: ptr.To[int32](11),
1215 Protocol: ptr.To(v1.ProtocolTCP),
1216 }},
1217 }
1218 }
1219
1220 createName := func(namespace, name string) types.NamespacedName {
1221 return types.NamespacedName{Namespace: namespace, Name: name}
1222 }
1223
1224 modifyEndpoints := func(slice *discovery.EndpointSlice, triggerTime time.Time) *discovery.EndpointSlice {
1225 e := slice.DeepCopy()
1226 (*e.Ports[0].Port)++
1227 e.Annotations[v1.EndpointsLastChangeTriggerTime] = triggerTime.Format(time.RFC3339Nano)
1228 return e
1229 }
1230
1231 testCases := []struct {
1232 name string
1233 scenario func(fp *FakeProxier)
1234 expected map[types.NamespacedName][]time.Time
1235 }{
1236 {
1237 name: "Single addEndpoints",
1238 scenario: func(fp *FakeProxier) {
1239 e := createEndpoints("ns", "ep1", t0)
1240 fp.addEndpointSlice(e)
1241 },
1242 expected: map[types.NamespacedName][]time.Time{createName("ns", "ep1"): {t0}},
1243 },
1244 {
1245 name: "addEndpoints then updatedEndpoints",
1246 scenario: func(fp *FakeProxier) {
1247 e := createEndpoints("ns", "ep1", t0)
1248 fp.addEndpointSlice(e)
1249
1250 e1 := modifyEndpoints(e, t1)
1251 fp.updateEndpointSlice(e, e1)
1252 },
1253 expected: map[types.NamespacedName][]time.Time{createName("ns", "ep1"): {t0, t1}},
1254 },
1255 {
1256 name: "Add two endpoints then modify one",
1257 scenario: func(fp *FakeProxier) {
1258 e1 := createEndpoints("ns", "ep1", t1)
1259 fp.addEndpointSlice(e1)
1260
1261 e2 := createEndpoints("ns", "ep2", t2)
1262 fp.addEndpointSlice(e2)
1263
1264 e11 := modifyEndpoints(e1, t3)
1265 fp.updateEndpointSlice(e1, e11)
1266 },
1267 expected: map[types.NamespacedName][]time.Time{createName("ns", "ep1"): {t1, t3}, createName("ns", "ep2"): {t2}},
1268 },
1269 {
1270 name: "Endpoints without annotation set",
1271 scenario: func(fp *FakeProxier) {
1272 e := createEndpoints("ns", "ep1", t1)
1273 delete(e.Annotations, v1.EndpointsLastChangeTriggerTime)
1274 fp.addEndpointSlice(e)
1275 },
1276 expected: map[types.NamespacedName][]time.Time{},
1277 },
1278 {
1279 name: "Endpoints create before tracker started",
1280 scenario: func(fp *FakeProxier) {
1281 e := createEndpoints("ns", "ep1", t_1)
1282 fp.addEndpointSlice(e)
1283 },
1284 expected: map[types.NamespacedName][]time.Time{},
1285 },
1286 {
1287 name: "addEndpoints then deleteEndpoints",
1288 scenario: func(fp *FakeProxier) {
1289 e := createEndpoints("ns", "ep1", t1)
1290 fp.addEndpointSlice(e)
1291 fp.deleteEndpointSlice(e)
1292 },
1293 expected: map[types.NamespacedName][]time.Time{},
1294 },
1295 {
1296 name: "add then delete then add again",
1297 scenario: func(fp *FakeProxier) {
1298 e := createEndpoints("ns", "ep1", t1)
1299 fp.addEndpointSlice(e)
1300 fp.deleteEndpointSlice(e)
1301 e = modifyEndpoints(e, t2)
1302 fp.addEndpointSlice(e)
1303 },
1304 expected: map[types.NamespacedName][]time.Time{createName("ns", "ep1"): {t2}},
1305 },
1306 {
1307 name: "delete",
1308 scenario: func(fp *FakeProxier) {
1309 e := createEndpoints("ns", "ep1", t1)
1310 fp.deleteEndpointSlice(e)
1311 },
1312 expected: map[types.NamespacedName][]time.Time{},
1313 },
1314 }
1315
1316 for _, tc := range testCases {
1317 fp := newFakeProxier(v1.IPv4Protocol, startTime)
1318
1319 tc.scenario(fp)
1320
1321 result := fp.endpointsMap.Update(fp.endpointsChanges)
1322 got := result.LastChangeTriggerTimes
1323
1324 if !reflect.DeepEqual(got, tc.expected) {
1325 t.Errorf("%s: Invalid LastChangeTriggerTimes, expected: %v, got: %v",
1326 tc.name, tc.expected, result.LastChangeTriggerTimes)
1327 }
1328 }
1329 }
1330
1331 func TestEndpointSliceUpdate(t *testing.T) {
1332 fqdnSlice := generateEndpointSlice("svc1", "ns1", 2, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)})
1333 fqdnSlice.AddressType = discovery.AddressTypeFQDN
1334
1335 testCases := map[string]struct {
1336 startingSlices []*discovery.EndpointSlice
1337 endpointsChangeTracker *EndpointsChangeTracker
1338 namespacedName types.NamespacedName
1339 paramEndpointSlice *discovery.EndpointSlice
1340 paramRemoveSlice bool
1341 expectedReturnVal bool
1342 expectedCurrentChange map[ServicePortName][]*BaseEndpointInfo
1343 }{
1344
1345 "add a simple slice that doesn't already exist": {
1346 startingSlices: []*discovery.EndpointSlice{},
1347 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1348 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1349 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1350 paramRemoveSlice: false,
1351 expectedReturnVal: true,
1352 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1353 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1354 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: false, ready: true, serving: true, terminating: false},
1355 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1356 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: false, ready: true, serving: true, terminating: false},
1357 },
1358 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1359 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: false, ready: true, serving: true, terminating: false},
1360 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1361 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: false, ready: true, serving: true, terminating: false},
1362 },
1363 },
1364 },
1365
1366 "add the same slice that already exists": {
1367 startingSlices: []*discovery.EndpointSlice{
1368 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1369 },
1370 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1371 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1372 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1373 paramRemoveSlice: false,
1374 expectedReturnVal: false,
1375 expectedCurrentChange: nil,
1376 },
1377
1378 "add an FQDN slice (invalid address type)": {
1379 startingSlices: []*discovery.EndpointSlice{
1380 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1381 },
1382 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1383 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1384 paramEndpointSlice: fqdnSlice,
1385 paramRemoveSlice: false,
1386 expectedReturnVal: false,
1387 expectedCurrentChange: nil,
1388 },
1389
1390 "add a slice that overlaps with existing state": {
1391 startingSlices: []*discovery.EndpointSlice{
1392 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1393 generateEndpointSlice("svc1", "ns1", 2, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1394 },
1395 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1396 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1397 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1398 paramRemoveSlice: false,
1399 expectedReturnVal: true,
1400 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1401 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1402 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1403 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1404 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: true, ready: true, serving: true, terminating: false},
1405 &BaseEndpointInfo{ip: "10.0.1.4", port: 80, endpoint: "10.0.1.4:80", isLocal: true, ready: true, serving: true, terminating: false},
1406 &BaseEndpointInfo{ip: "10.0.1.5", port: 80, endpoint: "10.0.1.5:80", isLocal: true, ready: true, serving: true, terminating: false},
1407 &BaseEndpointInfo{ip: "10.0.2.1", port: 80, endpoint: "10.0.2.1:80", isLocal: false, ready: true, serving: true, terminating: false},
1408 &BaseEndpointInfo{ip: "10.0.2.2", port: 80, endpoint: "10.0.2.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1409 },
1410 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1411 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1412 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1413 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: true, ready: true, serving: true, terminating: false},
1414 &BaseEndpointInfo{ip: "10.0.1.4", port: 443, endpoint: "10.0.1.4:443", isLocal: true, ready: true, serving: true, terminating: false},
1415 &BaseEndpointInfo{ip: "10.0.1.5", port: 443, endpoint: "10.0.1.5:443", isLocal: true, ready: true, serving: true, terminating: false},
1416 &BaseEndpointInfo{ip: "10.0.2.1", port: 443, endpoint: "10.0.2.1:443", isLocal: false, ready: true, serving: true, terminating: false},
1417 &BaseEndpointInfo{ip: "10.0.2.2", port: 443, endpoint: "10.0.2.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1418 },
1419 },
1420 },
1421
1422 "add a slice that overlaps with existing state and partial ports": {
1423 startingSlices: []*discovery.EndpointSlice{
1424 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1425 generateEndpointSlice("svc1", "ns1", 2, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1426 },
1427 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1428 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1429 paramEndpointSlice: generateEndpointSliceWithOffset("svc1", "ns1", 3, 1, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80)}),
1430 paramRemoveSlice: false,
1431 expectedReturnVal: true,
1432 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1433 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1434 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1435 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1436 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: true, ready: true, serving: true, terminating: false},
1437 &BaseEndpointInfo{ip: "10.0.1.4", port: 80, endpoint: "10.0.1.4:80", isLocal: true, ready: true, serving: true, terminating: false},
1438 &BaseEndpointInfo{ip: "10.0.1.5", port: 80, endpoint: "10.0.1.5:80", isLocal: true, ready: true, serving: true, terminating: false},
1439 &BaseEndpointInfo{ip: "10.0.2.1", port: 80, endpoint: "10.0.2.1:80", isLocal: false, ready: true, serving: true, terminating: false},
1440 &BaseEndpointInfo{ip: "10.0.2.2", port: 80, endpoint: "10.0.2.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1441 },
1442 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1443 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: false, ready: true, serving: true, terminating: false},
1444 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1445 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: false, ready: true, serving: true, terminating: false},
1446 &BaseEndpointInfo{ip: "10.0.2.1", port: 443, endpoint: "10.0.2.1:443", isLocal: false, ready: true, serving: true, terminating: false},
1447 &BaseEndpointInfo{ip: "10.0.2.2", port: 443, endpoint: "10.0.2.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1448 },
1449 },
1450 },
1451
1452 "remove a slice that overlaps with existing state": {
1453 startingSlices: []*discovery.EndpointSlice{
1454 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1455 generateEndpointSlice("svc1", "ns1", 2, 2, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1456 },
1457 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1458 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1459 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1460 paramRemoveSlice: true,
1461 expectedReturnVal: true,
1462 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1463 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1464 &BaseEndpointInfo{ip: "10.0.2.1", port: 80, endpoint: "10.0.2.1:80", isLocal: false, ready: true, serving: true, terminating: false},
1465 &BaseEndpointInfo{ip: "10.0.2.2", port: 80, endpoint: "10.0.2.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1466 },
1467 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1468 &BaseEndpointInfo{ip: "10.0.2.1", port: 443, endpoint: "10.0.2.1:443", isLocal: false, ready: true, serving: true, terminating: false},
1469 &BaseEndpointInfo{ip: "10.0.2.2", port: 443, endpoint: "10.0.2.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1470 },
1471 },
1472 },
1473
1474 "remove a slice that doesn't even exist in current state": {
1475 startingSlices: []*discovery.EndpointSlice{
1476 generateEndpointSlice("svc1", "ns1", 1, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1477 generateEndpointSlice("svc1", "ns1", 2, 2, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1478 },
1479 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1480 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1481 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 3, 5, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1482 paramRemoveSlice: true,
1483 expectedReturnVal: false,
1484 expectedCurrentChange: nil,
1485 },
1486
1487 "transition all endpoints to unready state": {
1488 startingSlices: []*discovery.EndpointSlice{
1489 generateEndpointSlice("svc1", "ns1", 1, 3, 999, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1490 },
1491 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1492 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1493 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 3, 1, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1494 paramRemoveSlice: false,
1495 expectedReturnVal: true,
1496 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1497 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1498 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: false, serving: false, terminating: false},
1499 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: false, serving: false, terminating: false},
1500 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: true, ready: false, serving: false, terminating: false},
1501 },
1502 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1503 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: true, ready: false, serving: false, terminating: false},
1504 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: false, serving: false, terminating: false},
1505 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: true, ready: false, serving: false, terminating: false},
1506 },
1507 },
1508 },
1509
1510 "transition all endpoints to ready state": {
1511 startingSlices: []*discovery.EndpointSlice{
1512 generateEndpointSlice("svc1", "ns1", 1, 2, 1, 999, []string{"host1", "host2"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1513 },
1514 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1515 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1516 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 2, 999, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1517 paramRemoveSlice: false,
1518 expectedReturnVal: true,
1519 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1520 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1521 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1522 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1523 },
1524 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1525 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1526 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1527 },
1528 },
1529 },
1530
1531 "transition some endpoints to ready state": {
1532 startingSlices: []*discovery.EndpointSlice{
1533 generateEndpointSlice("svc1", "ns1", 1, 3, 2, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1534 generateEndpointSlice("svc1", "ns1", 2, 2, 2, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1535 },
1536 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1537 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1538 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 3, 3, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1539 paramRemoveSlice: false,
1540 expectedReturnVal: true,
1541 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1542 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1543 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1544 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: true, serving: true, terminating: false},
1545 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: true, ready: false, serving: false, terminating: false},
1546 &BaseEndpointInfo{ip: "10.0.2.1", port: 80, endpoint: "10.0.2.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1547 &BaseEndpointInfo{ip: "10.0.2.2", port: 80, endpoint: "10.0.2.2:80", isLocal: true, ready: false, serving: false, terminating: false},
1548 },
1549 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1550 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1551 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: true, serving: true, terminating: false},
1552 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: true, ready: false, serving: false, terminating: false},
1553 &BaseEndpointInfo{ip: "10.0.2.1", port: 443, endpoint: "10.0.2.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1554 &BaseEndpointInfo{ip: "10.0.2.2", port: 443, endpoint: "10.0.2.2:443", isLocal: true, ready: false, serving: false, terminating: false},
1555 },
1556 },
1557 },
1558
1559 "transition some endpoints to terminating state": {
1560 startingSlices: []*discovery.EndpointSlice{
1561 generateEndpointSlice("svc1", "ns1", 1, 3, 2, 2, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1562 generateEndpointSlice("svc1", "ns1", 2, 2, 2, 2, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1563 },
1564 endpointsChangeTracker: NewEndpointsChangeTracker("host1", nil, v1.IPv4Protocol, nil, nil),
1565 namespacedName: types.NamespacedName{Name: "svc1", Namespace: "ns1"},
1566 paramEndpointSlice: generateEndpointSlice("svc1", "ns1", 1, 3, 3, 2, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1567 paramRemoveSlice: false,
1568 expectedReturnVal: true,
1569 expectedCurrentChange: map[ServicePortName][]*BaseEndpointInfo{
1570 makeServicePortName("ns1", "svc1", "port-0", v1.ProtocolTCP): {
1571 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1572 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", isLocal: true, ready: false, serving: true, terminating: true},
1573 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", isLocal: true, ready: false, serving: false, terminating: false},
1574 &BaseEndpointInfo{ip: "10.0.2.1", port: 80, endpoint: "10.0.2.1:80", isLocal: true, ready: true, serving: true, terminating: false},
1575 &BaseEndpointInfo{ip: "10.0.2.2", port: 80, endpoint: "10.0.2.2:80", isLocal: true, ready: false, serving: false, terminating: true},
1576 },
1577 makeServicePortName("ns1", "svc1", "port-1", v1.ProtocolTCP): {
1578 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1579 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", isLocal: true, ready: false, serving: true, terminating: true},
1580 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", isLocal: true, ready: false, serving: false, terminating: false},
1581 &BaseEndpointInfo{ip: "10.0.2.1", port: 443, endpoint: "10.0.2.1:443", isLocal: true, ready: true, serving: true, terminating: false},
1582 &BaseEndpointInfo{ip: "10.0.2.2", port: 443, endpoint: "10.0.2.2:443", isLocal: true, ready: false, serving: false, terminating: true},
1583 },
1584 },
1585 },
1586 }
1587
1588 for name, tc := range testCases {
1589 t.Run(name, func(t *testing.T) {
1590 initializeCache(tc.endpointsChangeTracker.endpointSliceCache, tc.startingSlices)
1591
1592 got := tc.endpointsChangeTracker.EndpointSliceUpdate(tc.paramEndpointSlice, tc.paramRemoveSlice)
1593 if !reflect.DeepEqual(got, tc.expectedReturnVal) {
1594 t.Errorf("EndpointSliceUpdate return value got: %v, want %v", got, tc.expectedReturnVal)
1595 }
1596
1597 changes := tc.endpointsChangeTracker.checkoutChanges()
1598 if tc.expectedCurrentChange == nil {
1599 if len(changes) != 0 {
1600 t.Errorf("Expected %s to have no changes", tc.namespacedName)
1601 }
1602 } else {
1603 if _, exists := changes[tc.namespacedName]; !exists {
1604 t.Fatalf("Expected %s to have changes", tc.namespacedName)
1605 }
1606 compareEndpointsMapsStr(t, changes[tc.namespacedName].current, tc.expectedCurrentChange)
1607 }
1608 })
1609 }
1610 }
1611
1612 func TestCheckoutChanges(t *testing.T) {
1613 svcPortName0 := ServicePortName{types.NamespacedName{Namespace: "ns1", Name: "svc1"}, "port-0", v1.ProtocolTCP}
1614 svcPortName1 := ServicePortName{types.NamespacedName{Namespace: "ns1", Name: "svc1"}, "port-1", v1.ProtocolTCP}
1615
1616 testCases := map[string]struct {
1617 endpointsChangeTracker *EndpointsChangeTracker
1618 expectedChanges []*endpointsChange
1619 items map[types.NamespacedName]*endpointsChange
1620 appliedSlices []*discovery.EndpointSlice
1621 pendingSlices []*discovery.EndpointSlice
1622 }{
1623 "empty slices": {
1624 endpointsChangeTracker: NewEndpointsChangeTracker("", nil, v1.IPv4Protocol, nil, nil),
1625 expectedChanges: []*endpointsChange{},
1626 appliedSlices: []*discovery.EndpointSlice{},
1627 pendingSlices: []*discovery.EndpointSlice{},
1628 },
1629 "adding initial slice": {
1630 endpointsChangeTracker: NewEndpointsChangeTracker("", nil, v1.IPv4Protocol, nil, nil),
1631 expectedChanges: []*endpointsChange{{
1632 previous: EndpointsMap{},
1633 current: EndpointsMap{
1634 svcPortName0: []Endpoint{
1635 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", ready: true, serving: true, terminating: false},
1636 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", ready: false, serving: true, terminating: true},
1637 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", ready: false, serving: false, terminating: false},
1638 },
1639 },
1640 }},
1641 appliedSlices: []*discovery.EndpointSlice{},
1642 pendingSlices: []*discovery.EndpointSlice{
1643 generateEndpointSlice("svc1", "ns1", 1, 3, 3, 2, []string{"host1"}, []*int32{ptr.To[int32](80)}),
1644 },
1645 },
1646 "removing port in update": {
1647 endpointsChangeTracker: NewEndpointsChangeTracker("", nil, v1.IPv4Protocol, nil, nil),
1648 expectedChanges: []*endpointsChange{{
1649 previous: EndpointsMap{
1650 svcPortName0: []Endpoint{
1651 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", ready: true, serving: true, terminating: false},
1652 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", ready: true, serving: true, terminating: false},
1653 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", ready: false, serving: false, terminating: false},
1654 },
1655 svcPortName1: []Endpoint{
1656 &BaseEndpointInfo{ip: "10.0.1.1", port: 443, endpoint: "10.0.1.1:443", ready: true, serving: true, terminating: false},
1657 &BaseEndpointInfo{ip: "10.0.1.2", port: 443, endpoint: "10.0.1.2:443", ready: true, serving: true, terminating: false},
1658 &BaseEndpointInfo{ip: "10.0.1.3", port: 443, endpoint: "10.0.1.3:443", ready: false, serving: false, terminating: false},
1659 },
1660 },
1661 current: EndpointsMap{
1662 svcPortName0: []Endpoint{
1663 &BaseEndpointInfo{ip: "10.0.1.1", port: 80, endpoint: "10.0.1.1:80", ready: true, serving: true, terminating: false},
1664 &BaseEndpointInfo{ip: "10.0.1.2", port: 80, endpoint: "10.0.1.2:80", ready: true, serving: true, terminating: false},
1665 &BaseEndpointInfo{ip: "10.0.1.3", port: 80, endpoint: "10.0.1.3:80", ready: false, serving: false, terminating: false},
1666 },
1667 },
1668 }},
1669 appliedSlices: []*discovery.EndpointSlice{
1670 generateEndpointSlice("svc1", "ns1", 1, 3, 3, 999, []string{"host1"}, []*int32{ptr.To[int32](80), ptr.To[int32](443)}),
1671 },
1672 pendingSlices: []*discovery.EndpointSlice{
1673 generateEndpointSlice("svc1", "ns1", 1, 3, 3, 999, []string{"host1"}, []*int32{ptr.To[int32](80)}),
1674 },
1675 },
1676 }
1677
1678 for name, tc := range testCases {
1679 t.Run(name, func(t *testing.T) {
1680 for _, slice := range tc.appliedSlices {
1681 tc.endpointsChangeTracker.EndpointSliceUpdate(slice, false)
1682 }
1683 tc.endpointsChangeTracker.checkoutChanges()
1684 for _, slice := range tc.pendingSlices {
1685 tc.endpointsChangeTracker.EndpointSliceUpdate(slice, false)
1686 }
1687 changes := tc.endpointsChangeTracker.checkoutChanges()
1688
1689 if len(tc.expectedChanges) != len(changes) {
1690 t.Fatalf("Expected %d changes, got %d", len(tc.expectedChanges), len(changes))
1691 }
1692
1693 for _, change := range changes {
1694
1695
1696 expectedChange := tc.expectedChanges[0]
1697
1698 if !reflect.DeepEqual(change.previous, expectedChange.previous) {
1699 t.Errorf("Expected change.previous: %+v, got: %+v", expectedChange.previous, change.previous)
1700 }
1701
1702 if !reflect.DeepEqual(change.current, expectedChange.current) {
1703 t.Errorf("Expected change.current: %+v, got: %+v", expectedChange.current, change.current)
1704 }
1705 }
1706 })
1707 }
1708 }
1709
1710
1711
1712 func compareEndpointsMapsStr(t *testing.T, newMap EndpointsMap, expected map[ServicePortName][]*BaseEndpointInfo) {
1713 t.Helper()
1714 if len(newMap) != len(expected) {
1715 t.Fatalf("expected %d results, got %d: %v", len(expected), len(newMap), newMap)
1716 }
1717 endpointEqual := func(a, b *BaseEndpointInfo) bool {
1718 return a.endpoint == b.endpoint && a.isLocal == b.isLocal && a.ready == b.ready && a.serving == b.serving && a.terminating == b.terminating
1719 }
1720 for x := range expected {
1721 if len(newMap[x]) != len(expected[x]) {
1722 t.Logf("Endpoints %+v", newMap[x])
1723 t.Fatalf("expected %d endpoints for %v, got %d", len(expected[x]), x, len(newMap[x]))
1724 } else {
1725 for i := range expected[x] {
1726 newEp, ok := newMap[x][i].(*BaseEndpointInfo)
1727 if !ok {
1728 t.Fatalf("Failed to cast endpointInfo")
1729 }
1730 if !endpointEqual(newEp, expected[x][i]) {
1731 t.Fatalf("expected new[%v][%d] to be %v, got %v"+
1732 "(IsLocal expected %v, got %v) (Ready expected %v, got %v) (Serving expected %v, got %v) (Terminating expected %v got %v)",
1733 x, i, expected[x][i], newEp, expected[x][i].isLocal, newEp.isLocal, expected[x][i].ready, newEp.ready,
1734 expected[x][i].serving, newEp.serving, expected[x][i].terminating, newEp.terminating)
1735 }
1736 }
1737 }
1738 }
1739 }
1740
1741 func initializeCache(endpointSliceCache *EndpointSliceCache, endpointSlices []*discovery.EndpointSlice) {
1742 for _, endpointSlice := range endpointSlices {
1743 endpointSliceCache.updatePending(endpointSlice, false)
1744 }
1745
1746 for _, tracker := range endpointSliceCache.trackerByServiceMap {
1747 tracker.applied = tracker.pending
1748 tracker.pending = endpointSliceDataByName{}
1749 }
1750 }
1751
View as plain text