k8s.io/kubernetes@v1.29.3/pkg/proxy/endpoints_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package 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 // Case[0]: nothing 51 endpointsMap: EndpointsMap{}, 52 expected: map[types.NamespacedName]sets.Set[string]{}, 53 }, { 54 // Case[1]: unnamed port 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 // Case[2]: unnamed port local 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 // Case[3]: named local and non-local ports for the same IP. 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 // Case[4]: named local and non-local ports for different IPs. 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 // Case[5]: named local and non-local ports for different IPs, some not ready. 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 // Case[6]: all endpoints are terminating,, so getLocalReadyEndpointIPs should return 0 ready endpoints 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 // outputs 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 // previousEndpointSlices and currentEndpointSlices are used to call appropriate 526 // handlers OnEndpointSlice* (based on whether corresponding values are nil 527 // or non-nil) and must be of equal length. 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 // First check that after adding all previous versions of endpoints, 1115 // the fp.previousEndpointsMap is as we expect. 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 // Now let's call appropriate handlers to get to state we want to be. 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 // test starting from an empty state 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 // test no modification to state - current change should be nil as nothing changes 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 // ensure that only valide address types are processed 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 // test additions to existing state 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 // test additions to existing state with partially overlapping slices and ports 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 // test deletions from existing state with partially overlapping slices and ports 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 // ensure a removal that has no effect turns into a no-op 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 // start with all endpoints ready, transition to no endpoints ready 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 // start with no endpoints ready, transition to all endpoints ready 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 // start with some endpoints ready, transition to more endpoints ready 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 // start with some endpoints ready, transition to some terminating 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 // All of the test cases have 0 or 1 changes, so if we're 1695 // here, then expectedChanges[0] is what we expect. 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 // Test helpers 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 = endpointSliceInfoByName{} 1749 } 1750 }