k8s.io/kubernetes@v1.29.3/pkg/proxy/service_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 "reflect" 21 "testing" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/dump" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 "k8s.io/apimachinery/pkg/util/sets" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 "k8s.io/kubernetes/pkg/features" 33 netutils "k8s.io/utils/net" 34 ) 35 36 const testHostname = "test-hostname" 37 38 func makeTestServiceInfo(clusterIP string, port int, protocol string, healthcheckNodePort int, svcInfoFuncs ...func(*BaseServicePortInfo)) *BaseServicePortInfo { 39 bsvcPortInfo := &BaseServicePortInfo{ 40 clusterIP: netutils.ParseIPSloppy(clusterIP), 41 port: port, 42 protocol: v1.Protocol(protocol), 43 } 44 if healthcheckNodePort != 0 { 45 bsvcPortInfo.healthCheckNodePort = healthcheckNodePort 46 } 47 for _, svcInfoFunc := range svcInfoFuncs { 48 svcInfoFunc(bsvcPortInfo) 49 } 50 return bsvcPortInfo 51 } 52 53 func makeTestService(namespace, name string, svcFunc func(*v1.Service)) *v1.Service { 54 svc := &v1.Service{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Name: name, 57 Namespace: namespace, 58 Annotations: map[string]string{}, 59 }, 60 Spec: v1.ServiceSpec{}, 61 Status: v1.ServiceStatus{}, 62 } 63 svcFunc(svc) 64 return svc 65 } 66 67 func addTestPort(array []v1.ServicePort, name string, protocol v1.Protocol, port, nodeport int32, targetPort int) []v1.ServicePort { 68 svcPort := v1.ServicePort{ 69 Name: name, 70 Protocol: protocol, 71 Port: port, 72 NodePort: nodeport, 73 TargetPort: intstr.FromInt32(int32(targetPort)), 74 } 75 return append(array, svcPort) 76 } 77 78 func makeNSN(namespace, name string) types.NamespacedName { 79 return types.NamespacedName{Namespace: namespace, Name: name} 80 } 81 82 func makeServicePortName(ns, name, port string, protocol v1.Protocol) ServicePortName { 83 return ServicePortName{ 84 NamespacedName: makeNSN(ns, name), 85 Port: port, 86 Protocol: protocol, 87 } 88 } 89 90 func TestServiceToServiceMap(t *testing.T) { 91 testClusterIPv4 := "10.0.0.1" 92 testExternalIPv4 := "8.8.8.8" 93 testSourceRangeIPv4 := "0.0.0.0/1" 94 testClusterIPv6 := "2001:db8:85a3:0:0:8a2e:370:7334" 95 testExternalIPv6 := "2001:db8:85a3:0:0:8a2e:370:7335" 96 testSourceRangeIPv6 := "2001:db8::/32" 97 ipModeVIP := v1.LoadBalancerIPModeVIP 98 ipModeProxy := v1.LoadBalancerIPModeProxy 99 100 testCases := []struct { 101 desc string 102 service *v1.Service 103 expected map[ServicePortName]*BaseServicePortInfo 104 ipFamily v1.IPFamily 105 ipModeEnabled bool 106 }{ 107 { 108 desc: "nothing", 109 ipFamily: v1.IPv4Protocol, 110 111 service: nil, 112 expected: map[ServicePortName]*BaseServicePortInfo{}, 113 }, 114 { 115 desc: "headless service", 116 ipFamily: v1.IPv4Protocol, 117 118 service: makeTestService("ns2", "headless", func(svc *v1.Service) { 119 svc.Spec.Type = v1.ServiceTypeClusterIP 120 svc.Spec.ClusterIP = v1.ClusterIPNone 121 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0) 122 }), 123 expected: map[ServicePortName]*BaseServicePortInfo{}, 124 }, 125 { 126 desc: "headless sctp service", 127 ipFamily: v1.IPv4Protocol, 128 129 service: makeTestService("ns2", "headless", func(svc *v1.Service) { 130 svc.Spec.Type = v1.ServiceTypeClusterIP 131 svc.Spec.ClusterIP = v1.ClusterIPNone 132 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sip", "SCTP", 7777, 0, 0) 133 }), 134 expected: map[ServicePortName]*BaseServicePortInfo{}, 135 }, 136 { 137 desc: "headless service without port", 138 ipFamily: v1.IPv4Protocol, 139 140 service: makeTestService("ns2", "headless-without-port", func(svc *v1.Service) { 141 svc.Spec.Type = v1.ServiceTypeClusterIP 142 svc.Spec.ClusterIP = v1.ClusterIPNone 143 }), 144 expected: map[ServicePortName]*BaseServicePortInfo{}, 145 }, 146 { 147 desc: "cluster ip service", 148 ipFamily: v1.IPv4Protocol, 149 150 service: makeTestService("ns2", "cluster-ip", func(svc *v1.Service) { 151 svc.Spec.Type = v1.ServiceTypeClusterIP 152 svc.Spec.ClusterIP = "172.16.55.4" 153 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p1", "UDP", 1234, 4321, 0) 154 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "UDP", 1235, 5321, 0) 155 }), 156 expected: map[ServicePortName]*BaseServicePortInfo{ 157 makeServicePortName("ns2", "cluster-ip", "p1", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.4", 1234, "UDP", 0), 158 makeServicePortName("ns2", "cluster-ip", "p2", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.4", 1235, "UDP", 0), 159 }, 160 }, 161 { 162 desc: "nodeport service", 163 ipFamily: v1.IPv4Protocol, 164 165 service: makeTestService("ns2", "node-port", func(svc *v1.Service) { 166 svc.Spec.Type = v1.ServiceTypeNodePort 167 svc.Spec.ClusterIP = "172.16.55.10" 168 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port1", "UDP", 345, 678, 0) 169 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port2", "TCP", 344, 677, 0) 170 }), 171 expected: map[ServicePortName]*BaseServicePortInfo{ 172 makeServicePortName("ns2", "node-port", "port1", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.10", 345, "UDP", 0), 173 makeServicePortName("ns2", "node-port", "port2", v1.ProtocolTCP): makeTestServiceInfo("172.16.55.10", 344, "TCP", 0), 174 }, 175 }, 176 { 177 desc: "load balancer service", 178 ipFamily: v1.IPv4Protocol, 179 180 service: makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 181 svc.Spec.Type = v1.ServiceTypeLoadBalancer 182 svc.Spec.ClusterIP = "172.16.55.11" 183 svc.Spec.LoadBalancerIP = "5.6.7.8" 184 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port3", "UDP", 8675, 30061, 7000) 185 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port4", "UDP", 8676, 30062, 7001) 186 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.4"}} 187 }), 188 expected: map[ServicePortName]*BaseServicePortInfo{ 189 makeServicePortName("ns1", "load-balancer", "port3", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 190 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 191 }), 192 makeServicePortName("ns1", "load-balancer", "port4", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 193 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 194 }), 195 }, 196 }, 197 { 198 desc: "load balancer service ipMode VIP feature gate disable", 199 ipFamily: v1.IPv4Protocol, 200 201 service: makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 202 svc.Spec.Type = v1.ServiceTypeLoadBalancer 203 svc.Spec.ClusterIP = "172.16.55.11" 204 svc.Spec.LoadBalancerIP = "5.6.7.8" 205 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port3", "UDP", 8675, 30061, 7000) 206 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port4", "UDP", 8676, 30062, 7001) 207 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.4", IPMode: &ipModeVIP}} 208 }), 209 expected: map[ServicePortName]*BaseServicePortInfo{ 210 makeServicePortName("ns1", "load-balancer", "port3", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 211 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 212 }), 213 makeServicePortName("ns1", "load-balancer", "port4", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 214 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 215 }), 216 }, 217 }, 218 { 219 desc: "load balancer service ipMode Proxy feature gate disable", 220 ipFamily: v1.IPv4Protocol, 221 222 service: makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 223 svc.Spec.Type = v1.ServiceTypeLoadBalancer 224 svc.Spec.ClusterIP = "172.16.55.11" 225 svc.Spec.LoadBalancerIP = "5.6.7.8" 226 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port3", "UDP", 8675, 30061, 7000) 227 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port4", "UDP", 8676, 30062, 7001) 228 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.4", IPMode: &ipModeProxy}} 229 }), 230 expected: map[ServicePortName]*BaseServicePortInfo{ 231 makeServicePortName("ns1", "load-balancer", "port3", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 232 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 233 }), 234 makeServicePortName("ns1", "load-balancer", "port4", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 235 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 236 }), 237 }, 238 }, 239 { 240 desc: "load balancer service ipMode VIP feature gate enabled", 241 ipFamily: v1.IPv4Protocol, 242 ipModeEnabled: true, 243 244 service: makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 245 svc.Spec.Type = v1.ServiceTypeLoadBalancer 246 svc.Spec.ClusterIP = "172.16.55.11" 247 svc.Spec.LoadBalancerIP = "5.6.7.8" 248 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port3", "UDP", 8675, 30061, 7000) 249 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port4", "UDP", 8676, 30062, 7001) 250 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.4", IPMode: &ipModeVIP}} 251 }), 252 expected: map[ServicePortName]*BaseServicePortInfo{ 253 makeServicePortName("ns1", "load-balancer", "port3", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 254 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 255 }), 256 makeServicePortName("ns1", "load-balancer", "port4", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 257 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.4"} 258 }), 259 }, 260 }, 261 { 262 desc: "load balancer service ipMode Proxy feature gate enabled", 263 ipFamily: v1.IPv4Protocol, 264 ipModeEnabled: true, 265 266 service: makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 267 svc.Spec.Type = v1.ServiceTypeLoadBalancer 268 svc.Spec.ClusterIP = "172.16.55.11" 269 svc.Spec.LoadBalancerIP = "5.6.7.8" 270 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port3", "UDP", 8675, 30061, 7000) 271 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port4", "UDP", 8676, 30062, 7001) 272 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.4", IPMode: &ipModeProxy}} 273 }), 274 expected: map[ServicePortName]*BaseServicePortInfo{ 275 makeServicePortName("ns1", "load-balancer", "port3", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 276 }), 277 makeServicePortName("ns1", "load-balancer", "port4", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 278 }), 279 }, 280 }, 281 { 282 desc: "load balancer service with only local traffic policy", 283 ipFamily: v1.IPv4Protocol, 284 285 service: makeTestService("ns1", "only-local-load-balancer", func(svc *v1.Service) { 286 svc.Spec.Type = v1.ServiceTypeLoadBalancer 287 svc.Spec.ClusterIP = "172.16.55.12" 288 svc.Spec.LoadBalancerIP = "5.6.7.8" 289 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "portx", "UDP", 8677, 30063, 7002) 290 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "porty", "UDP", 8678, 30064, 7003) 291 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{IP: "10.1.2.3"}} 292 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 293 svc.Spec.HealthCheckNodePort = 345 294 }), 295 expected: map[ServicePortName]*BaseServicePortInfo{ 296 makeServicePortName("ns1", "only-local-load-balancer", "portx", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.12", 8677, "UDP", 345, func(bsvcPortInfo *BaseServicePortInfo) { 297 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.3"} 298 }), 299 makeServicePortName("ns1", "only-local-load-balancer", "porty", v1.ProtocolUDP): makeTestServiceInfo("172.16.55.12", 8678, "UDP", 345, func(bsvcPortInfo *BaseServicePortInfo) { 300 bsvcPortInfo.loadBalancerVIPs = []string{"10.1.2.3"} 301 }), 302 }, 303 }, 304 { 305 desc: "external name service", 306 ipFamily: v1.IPv4Protocol, 307 308 service: makeTestService("ns2", "external-name", func(svc *v1.Service) { 309 svc.Spec.Type = v1.ServiceTypeExternalName 310 svc.Spec.ClusterIP = "172.16.55.4" // Should be ignored 311 svc.Spec.ExternalName = "foo2.bar.com" 312 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "portz", "UDP", 1235, 5321, 0) 313 }), 314 expected: map[ServicePortName]*BaseServicePortInfo{}, 315 }, 316 { 317 desc: "service with ipv6 clusterIP under ipv4 mode, service should be filtered", 318 ipFamily: v1.IPv4Protocol, 319 320 service: &v1.Service{ 321 ObjectMeta: metav1.ObjectMeta{ 322 Name: "invalidIPv6InIPV4Mode", 323 Namespace: "test", 324 }, 325 Spec: v1.ServiceSpec{ 326 ClusterIP: testClusterIPv6, 327 Ports: []v1.ServicePort{ 328 { 329 Name: "testPort", 330 Port: int32(12345), 331 Protocol: v1.ProtocolTCP, 332 }, 333 }, 334 }, 335 Status: v1.ServiceStatus{ 336 LoadBalancer: v1.LoadBalancerStatus{ 337 Ingress: []v1.LoadBalancerIngress{ 338 {IP: testExternalIPv4}, 339 {IP: testExternalIPv6}, 340 }, 341 }, 342 }, 343 }, 344 }, 345 { 346 desc: "service with ipv4 clusterIP under ipv6 mode, service should be filtered", 347 ipFamily: v1.IPv6Protocol, 348 349 service: &v1.Service{ 350 ObjectMeta: metav1.ObjectMeta{ 351 Name: "invalidIPv4InIPV6Mode", 352 Namespace: "test", 353 }, 354 Spec: v1.ServiceSpec{ 355 ClusterIP: testClusterIPv4, 356 Ports: []v1.ServicePort{ 357 { 358 Name: "testPort", 359 Port: int32(12345), 360 Protocol: v1.ProtocolTCP, 361 }, 362 }, 363 }, 364 Status: v1.ServiceStatus{ 365 LoadBalancer: v1.LoadBalancerStatus{ 366 Ingress: []v1.LoadBalancerIngress{ 367 {IP: testExternalIPv4}, 368 {IP: testExternalIPv6}, 369 }, 370 }, 371 }, 372 }, 373 }, 374 { 375 desc: "service with ipv4 configurations under ipv4 mode", 376 ipFamily: v1.IPv4Protocol, 377 378 service: &v1.Service{ 379 ObjectMeta: metav1.ObjectMeta{ 380 Name: "validIPv4", 381 Namespace: "test", 382 }, 383 Spec: v1.ServiceSpec{ 384 ClusterIP: testClusterIPv4, 385 ExternalIPs: []string{testExternalIPv4}, 386 LoadBalancerSourceRanges: []string{testSourceRangeIPv4}, 387 Ports: []v1.ServicePort{ 388 { 389 Name: "testPort", 390 Port: int32(12345), 391 Protocol: v1.ProtocolTCP, 392 }, 393 }, 394 }, 395 Status: v1.ServiceStatus{ 396 LoadBalancer: v1.LoadBalancerStatus{ 397 Ingress: []v1.LoadBalancerIngress{ 398 {IP: testExternalIPv4}, 399 {IP: testExternalIPv6}, 400 }, 401 }, 402 }, 403 }, 404 expected: map[ServicePortName]*BaseServicePortInfo{ 405 makeServicePortName("test", "validIPv4", "testPort", v1.ProtocolTCP): makeTestServiceInfo(testClusterIPv4, 12345, "TCP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 406 bsvcPortInfo.externalIPs = []string{testExternalIPv4} 407 bsvcPortInfo.loadBalancerSourceRanges = []string{testSourceRangeIPv4} 408 bsvcPortInfo.loadBalancerVIPs = []string{testExternalIPv4} 409 }), 410 }, 411 }, 412 { 413 desc: "service with ipv6 configurations under ipv6 mode", 414 ipFamily: v1.IPv6Protocol, 415 416 service: &v1.Service{ 417 ObjectMeta: metav1.ObjectMeta{ 418 Name: "validIPv6", 419 Namespace: "test", 420 }, 421 Spec: v1.ServiceSpec{ 422 ClusterIP: testClusterIPv6, 423 ExternalIPs: []string{testExternalIPv6}, 424 LoadBalancerSourceRanges: []string{testSourceRangeIPv6}, 425 Ports: []v1.ServicePort{ 426 { 427 Name: "testPort", 428 Port: int32(12345), 429 Protocol: v1.ProtocolTCP, 430 }, 431 }, 432 }, 433 Status: v1.ServiceStatus{ 434 LoadBalancer: v1.LoadBalancerStatus{ 435 Ingress: []v1.LoadBalancerIngress{ 436 {IP: testExternalIPv4}, 437 {IP: testExternalIPv6}, 438 }, 439 }, 440 }, 441 }, 442 expected: map[ServicePortName]*BaseServicePortInfo{ 443 makeServicePortName("test", "validIPv6", "testPort", v1.ProtocolTCP): makeTestServiceInfo(testClusterIPv6, 12345, "TCP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 444 bsvcPortInfo.externalIPs = []string{testExternalIPv6} 445 bsvcPortInfo.loadBalancerSourceRanges = []string{testSourceRangeIPv6} 446 bsvcPortInfo.loadBalancerVIPs = []string{testExternalIPv6} 447 }), 448 }, 449 }, 450 { 451 desc: "service with both ipv4 and ipv6 configurations under ipv4 mode, ipv6 fields should be filtered", 452 ipFamily: v1.IPv4Protocol, 453 454 service: &v1.Service{ 455 ObjectMeta: metav1.ObjectMeta{ 456 Name: "filterIPv6InIPV4Mode", 457 Namespace: "test", 458 }, 459 Spec: v1.ServiceSpec{ 460 ClusterIP: testClusterIPv4, 461 ExternalIPs: []string{testExternalIPv4, testExternalIPv6}, 462 LoadBalancerSourceRanges: []string{testSourceRangeIPv4, testSourceRangeIPv6}, 463 Ports: []v1.ServicePort{ 464 { 465 Name: "testPort", 466 Port: int32(12345), 467 Protocol: v1.ProtocolTCP, 468 }, 469 }, 470 }, 471 Status: v1.ServiceStatus{ 472 LoadBalancer: v1.LoadBalancerStatus{ 473 Ingress: []v1.LoadBalancerIngress{ 474 {IP: testExternalIPv4}, 475 {IP: testExternalIPv6}, 476 }, 477 }, 478 }, 479 }, 480 expected: map[ServicePortName]*BaseServicePortInfo{ 481 makeServicePortName("test", "filterIPv6InIPV4Mode", "testPort", v1.ProtocolTCP): makeTestServiceInfo(testClusterIPv4, 12345, "TCP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 482 bsvcPortInfo.externalIPs = []string{testExternalIPv4} 483 bsvcPortInfo.loadBalancerSourceRanges = []string{testSourceRangeIPv4} 484 bsvcPortInfo.loadBalancerVIPs = []string{testExternalIPv4} 485 }), 486 }, 487 }, 488 { 489 desc: "service with both ipv4 and ipv6 configurations under ipv6 mode, ipv4 fields should be filtered", 490 ipFamily: v1.IPv6Protocol, 491 492 service: &v1.Service{ 493 ObjectMeta: metav1.ObjectMeta{ 494 Name: "filterIPv4InIPV6Mode", 495 Namespace: "test", 496 }, 497 Spec: v1.ServiceSpec{ 498 ClusterIP: testClusterIPv6, 499 ExternalIPs: []string{testExternalIPv4, testExternalIPv6}, 500 LoadBalancerSourceRanges: []string{testSourceRangeIPv4, testSourceRangeIPv6}, 501 Ports: []v1.ServicePort{ 502 { 503 Name: "testPort", 504 Port: int32(12345), 505 Protocol: v1.ProtocolTCP, 506 }, 507 }, 508 }, 509 Status: v1.ServiceStatus{ 510 LoadBalancer: v1.LoadBalancerStatus{ 511 Ingress: []v1.LoadBalancerIngress{ 512 {IP: testExternalIPv4}, 513 {IP: testExternalIPv6}, 514 }, 515 }, 516 }, 517 }, 518 expected: map[ServicePortName]*BaseServicePortInfo{ 519 makeServicePortName("test", "filterIPv4InIPV6Mode", "testPort", v1.ProtocolTCP): makeTestServiceInfo(testClusterIPv6, 12345, "TCP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 520 bsvcPortInfo.externalIPs = []string{testExternalIPv6} 521 bsvcPortInfo.loadBalancerSourceRanges = []string{testSourceRangeIPv6} 522 bsvcPortInfo.loadBalancerVIPs = []string{testExternalIPv6} 523 }), 524 }, 525 }, 526 { 527 desc: "service with extra space in LoadBalancerSourceRanges", 528 ipFamily: v1.IPv4Protocol, 529 530 service: &v1.Service{ 531 ObjectMeta: metav1.ObjectMeta{ 532 Name: "extra-space", 533 Namespace: "test", 534 }, 535 Spec: v1.ServiceSpec{ 536 ClusterIP: testClusterIPv4, 537 LoadBalancerSourceRanges: []string{" 10.1.2.0/28"}, 538 Ports: []v1.ServicePort{ 539 { 540 Name: "testPort", 541 Port: int32(12345), 542 Protocol: v1.ProtocolTCP, 543 }, 544 }, 545 }, 546 }, 547 expected: map[ServicePortName]*BaseServicePortInfo{ 548 makeServicePortName("test", "extra-space", "testPort", v1.ProtocolTCP): makeTestServiceInfo(testClusterIPv4, 12345, "TCP", 0, func(bsvcPortInfo *BaseServicePortInfo) { 549 bsvcPortInfo.loadBalancerSourceRanges = []string{"10.1.2.0/28"} 550 }), 551 }, 552 }, 553 } 554 555 for _, tc := range testCases { 556 t.Run(tc.desc, func(t *testing.T) { 557 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled)() 558 svcTracker := NewServiceChangeTracker(nil, tc.ipFamily, nil, nil) 559 // outputs 560 newServices := svcTracker.serviceToServiceMap(tc.service) 561 562 if len(newServices) != len(tc.expected) { 563 t.Fatalf("expected %d new, got %d: %v", len(tc.expected), len(newServices), dump.Pretty(newServices)) 564 } 565 for svcKey, expectedInfo := range tc.expected { 566 svcInfo, exists := newServices[svcKey].(*BaseServicePortInfo) 567 if !exists { 568 t.Fatalf("[%s] expected to find key %s", tc.desc, svcKey) 569 } 570 571 if !svcInfo.clusterIP.Equal(expectedInfo.clusterIP) || 572 svcInfo.port != expectedInfo.port || 573 svcInfo.protocol != expectedInfo.protocol || 574 svcInfo.healthCheckNodePort != expectedInfo.healthCheckNodePort || 575 !sets.New[string](svcInfo.externalIPs...).Equal(sets.New[string](expectedInfo.externalIPs...)) || 576 !sets.New[string](svcInfo.loadBalancerSourceRanges...).Equal(sets.New[string](expectedInfo.loadBalancerSourceRanges...)) || 577 !reflect.DeepEqual(svcInfo.loadBalancerVIPs, expectedInfo.loadBalancerVIPs) { 578 t.Errorf("[%s] expected new[%v]to be %v, got %v", tc.desc, svcKey, expectedInfo, *svcInfo) 579 } 580 for svcKey, expectedInfo := range tc.expected { 581 svcInfo, _ := newServices[svcKey].(*BaseServicePortInfo) 582 if !svcInfo.clusterIP.Equal(expectedInfo.clusterIP) || 583 svcInfo.port != expectedInfo.port || 584 svcInfo.protocol != expectedInfo.protocol || 585 svcInfo.healthCheckNodePort != expectedInfo.healthCheckNodePort || 586 !sets.New[string](svcInfo.externalIPs...).Equal(sets.New[string](expectedInfo.externalIPs...)) || 587 !sets.New[string](svcInfo.loadBalancerSourceRanges...).Equal(sets.New[string](expectedInfo.loadBalancerSourceRanges...)) || 588 !reflect.DeepEqual(svcInfo.loadBalancerVIPs, expectedInfo.loadBalancerVIPs) { 589 t.Errorf("expected new[%v]to be %v, got %v", svcKey, expectedInfo, *svcInfo) 590 } 591 } 592 } 593 }) 594 } 595 } 596 597 type FakeProxier struct { 598 endpointsChanges *EndpointsChangeTracker 599 serviceChanges *ServiceChangeTracker 600 svcPortMap ServicePortMap 601 endpointsMap EndpointsMap 602 hostname string 603 } 604 605 func newFakeProxier(ipFamily v1.IPFamily, t time.Time) *FakeProxier { 606 return &FakeProxier{ 607 svcPortMap: make(ServicePortMap), 608 serviceChanges: NewServiceChangeTracker(nil, ipFamily, nil, nil), 609 endpointsMap: make(EndpointsMap), 610 endpointsChanges: &EndpointsChangeTracker{ 611 lastChangeTriggerTimes: make(map[types.NamespacedName][]time.Time), 612 trackerStartTime: t, 613 processEndpointsMapChange: nil, 614 endpointSliceCache: NewEndpointSliceCache(testHostname, ipFamily, nil, nil), 615 }, 616 } 617 } 618 619 func makeServiceMap(fake *FakeProxier, allServices ...*v1.Service) { 620 for i := range allServices { 621 fake.addService(allServices[i]) 622 } 623 } 624 625 func (proxier *FakeProxier) addService(service *v1.Service) { 626 proxier.serviceChanges.Update(nil, service) 627 } 628 629 func (proxier *FakeProxier) updateService(oldService *v1.Service, service *v1.Service) { 630 proxier.serviceChanges.Update(oldService, service) 631 } 632 633 func (proxier *FakeProxier) deleteService(service *v1.Service) { 634 proxier.serviceChanges.Update(service, nil) 635 } 636 637 func TestServiceMapUpdateHeadless(t *testing.T) { 638 fp := newFakeProxier(v1.IPv4Protocol, time.Time{}) 639 640 makeServiceMap(fp, 641 makeTestService("ns2", "headless", func(svc *v1.Service) { 642 svc.Spec.Type = v1.ServiceTypeClusterIP 643 svc.Spec.ClusterIP = v1.ClusterIPNone 644 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0) 645 }), 646 makeTestService("ns2", "headless-without-port", func(svc *v1.Service) { 647 svc.Spec.Type = v1.ServiceTypeClusterIP 648 svc.Spec.ClusterIP = v1.ClusterIPNone 649 }), 650 ) 651 652 // Headless service should be ignored 653 result := fp.svcPortMap.Update(fp.serviceChanges) 654 if len(fp.svcPortMap) != 0 { 655 t.Errorf("expected service map length 0, got %d", len(fp.svcPortMap)) 656 } 657 658 if len(result.UpdatedServices) != 0 { 659 t.Errorf("expected 0 updated services, got %d", len(result.UpdatedServices)) 660 } 661 if len(result.DeletedUDPClusterIPs) != 0 { 662 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 663 } 664 665 // No proxied services, so no healthchecks 666 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 667 if len(healthCheckNodePorts) != 0 { 668 t.Errorf("expected healthcheck ports length 0, got %d", len(healthCheckNodePorts)) 669 } 670 } 671 672 func TestUpdateServiceTypeExternalName(t *testing.T) { 673 fp := newFakeProxier(v1.IPv4Protocol, time.Time{}) 674 675 makeServiceMap(fp, 676 makeTestService("ns2", "external-name", func(svc *v1.Service) { 677 svc.Spec.Type = v1.ServiceTypeExternalName 678 svc.Spec.ClusterIP = "172.16.55.4" // Should be ignored 679 svc.Spec.ExternalName = "foo2.bar.com" 680 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blah", "UDP", 1235, 5321, 0) 681 }), 682 ) 683 684 result := fp.svcPortMap.Update(fp.serviceChanges) 685 if len(fp.svcPortMap) != 0 { 686 t.Errorf("expected service map length 0, got %v", fp.svcPortMap) 687 } 688 if len(result.UpdatedServices) != 0 { 689 t.Errorf("expected 0 updated services, got %v", result.UpdatedServices) 690 } 691 if len(result.DeletedUDPClusterIPs) != 0 { 692 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs) 693 } 694 695 // No proxied services, so no healthchecks 696 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 697 if len(healthCheckNodePorts) != 0 { 698 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 699 } 700 } 701 702 func TestBuildServiceMapAddRemove(t *testing.T) { 703 fp := newFakeProxier(v1.IPv4Protocol, time.Time{}) 704 705 services := []*v1.Service{ 706 makeTestService("ns2", "cluster-ip", func(svc *v1.Service) { 707 svc.Spec.Type = v1.ServiceTypeClusterIP 708 svc.Spec.ClusterIP = "172.16.55.4" 709 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port1", "UDP", 1234, 4321, 0) 710 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port2", "UDP", 1235, 5321, 0) 711 }), 712 makeTestService("ns2", "node-port", func(svc *v1.Service) { 713 svc.Spec.Type = v1.ServiceTypeNodePort 714 svc.Spec.ClusterIP = "172.16.55.10" 715 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port1", "UDP", 345, 678, 0) 716 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port2", "TCP", 344, 677, 0) 717 }), 718 makeTestService("ns1", "load-balancer", func(svc *v1.Service) { 719 svc.Spec.Type = v1.ServiceTypeLoadBalancer 720 svc.Spec.ClusterIP = "172.16.55.11" 721 svc.Spec.LoadBalancerIP = "5.6.7.8" 722 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar", "UDP", 8675, 30061, 7000) 723 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8676, 30062, 7001) 724 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 725 Ingress: []v1.LoadBalancerIngress{ 726 {IP: "10.1.2.4"}, 727 }, 728 } 729 }), 730 makeTestService("ns1", "only-local-load-balancer", func(svc *v1.Service) { 731 svc.Spec.Type = v1.ServiceTypeLoadBalancer 732 svc.Spec.ClusterIP = "172.16.55.12" 733 svc.Spec.LoadBalancerIP = "5.6.7.8" 734 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar2", "UDP", 8677, 30063, 7002) 735 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8678, 30064, 7003) 736 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 737 Ingress: []v1.LoadBalancerIngress{ 738 {IP: "10.1.2.3"}, 739 }, 740 } 741 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 742 svc.Spec.HealthCheckNodePort = 345 743 }), 744 } 745 746 for i := range services { 747 fp.addService(services[i]) 748 } 749 750 result := fp.svcPortMap.Update(fp.serviceChanges) 751 if len(fp.svcPortMap) != 8 { 752 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 753 } 754 for i := range services { 755 name := makeNSN(services[i].Namespace, services[i].Name) 756 if !result.UpdatedServices.Has(name) { 757 t.Errorf("expected updated service for %q", name) 758 } 759 } 760 if len(result.UpdatedServices) != len(services) { 761 t.Errorf("expected %d updated services, got %d", len(services), len(result.UpdatedServices)) 762 } 763 if len(result.DeletedUDPClusterIPs) != 0 { 764 // Services only added, so nothing stale yet 765 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 766 } 767 768 // The only-local-loadbalancer ones get added 769 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 770 if len(healthCheckNodePorts) != 1 { 771 t.Errorf("expected 1 healthcheck port, got %v", healthCheckNodePorts) 772 } else { 773 nsn := makeNSN("ns1", "only-local-load-balancer") 774 if port, found := healthCheckNodePorts[nsn]; !found || port != 345 { 775 t.Errorf("expected healthcheck port [%q]=345: got %v", nsn, healthCheckNodePorts) 776 } 777 } 778 779 // Remove some stuff 780 // oneService is a modification of services[0] with removed first port. 781 oneService := makeTestService("ns2", "cluster-ip", func(svc *v1.Service) { 782 svc.Spec.Type = v1.ServiceTypeClusterIP 783 svc.Spec.ClusterIP = "172.16.55.4" 784 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "UDP", 1235, 5321, 0) 785 }) 786 787 fp.updateService(services[0], oneService) 788 fp.deleteService(services[1]) 789 fp.deleteService(services[2]) 790 fp.deleteService(services[3]) 791 792 result = fp.svcPortMap.Update(fp.serviceChanges) 793 if len(fp.svcPortMap) != 1 { 794 t.Errorf("expected service map length 1, got %v", fp.svcPortMap) 795 } 796 if len(result.UpdatedServices) != 4 { 797 t.Errorf("expected 4 updated services, got %d", len(result.UpdatedServices)) 798 } 799 800 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 801 if len(healthCheckNodePorts) != 0 { 802 t.Errorf("expected 0 healthcheck ports, got %v", healthCheckNodePorts) 803 } 804 805 // All services but one were deleted. While you'd expect only the ClusterIPs 806 // from the three deleted services here, we still have the ClusterIP for 807 // the not-deleted service, because one of it's ServicePorts was deleted. 808 expectedDeletedUDPClusterIPs := []string{"172.16.55.10", "172.16.55.4", "172.16.55.11", "172.16.55.12"} 809 if len(result.DeletedUDPClusterIPs) != len(expectedDeletedUDPClusterIPs) { 810 t.Errorf("expected stale UDP services length %d, got %v", len(expectedDeletedUDPClusterIPs), result.DeletedUDPClusterIPs.UnsortedList()) 811 } 812 for _, ip := range expectedDeletedUDPClusterIPs { 813 if !result.DeletedUDPClusterIPs.Has(ip) { 814 t.Errorf("expected stale UDP service service %s", ip) 815 } 816 } 817 } 818 819 func TestBuildServiceMapServiceUpdate(t *testing.T) { 820 fp := newFakeProxier(v1.IPv4Protocol, time.Time{}) 821 822 servicev1 := makeTestService("ns1", "svc1", func(svc *v1.Service) { 823 svc.Spec.Type = v1.ServiceTypeClusterIP 824 svc.Spec.ClusterIP = "172.16.55.4" 825 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p1", "UDP", 1234, 4321, 0) 826 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "TCP", 1235, 5321, 0) 827 }) 828 servicev2 := makeTestService("ns1", "svc1", func(svc *v1.Service) { 829 svc.Spec.Type = v1.ServiceTypeLoadBalancer 830 svc.Spec.ClusterIP = "172.16.55.4" 831 svc.Spec.LoadBalancerIP = "5.6.7.8" 832 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p1", "UDP", 1234, 4321, 7002) 833 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "TCP", 1235, 5321, 7003) 834 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 835 Ingress: []v1.LoadBalancerIngress{ 836 {IP: "10.1.2.3"}, 837 }, 838 } 839 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 840 svc.Spec.HealthCheckNodePort = 345 841 }) 842 843 fp.addService(servicev1) 844 845 result := fp.svcPortMap.Update(fp.serviceChanges) 846 if len(fp.svcPortMap) != 2 { 847 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 848 } 849 if len(result.UpdatedServices) != 1 { 850 t.Errorf("expected 1 updated service, got %d", len(result.UpdatedServices)) 851 } 852 if len(result.DeletedUDPClusterIPs) != 0 { 853 // Services only added, so nothing stale yet 854 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 855 } 856 857 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 858 if len(healthCheckNodePorts) != 0 { 859 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 860 } 861 862 // Change service to load-balancer 863 fp.updateService(servicev1, servicev2) 864 result = fp.svcPortMap.Update(fp.serviceChanges) 865 if len(fp.svcPortMap) != 2 { 866 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 867 } 868 if len(result.UpdatedServices) != 1 { 869 t.Errorf("expected 1 updated service, got %d", len(result.UpdatedServices)) 870 } 871 if len(result.DeletedUDPClusterIPs) != 0 { 872 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList()) 873 } 874 875 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 876 if len(healthCheckNodePorts) != 1 { 877 t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts) 878 } 879 880 // No change; make sure the service map stays the same and there are 881 // no health-check changes 882 fp.updateService(servicev2, servicev2) 883 result = fp.svcPortMap.Update(fp.serviceChanges) 884 if len(fp.svcPortMap) != 2 { 885 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 886 } 887 if len(result.UpdatedServices) != 0 { 888 t.Errorf("expected 0 updated services, got %d", len(result.UpdatedServices)) 889 } 890 if len(result.DeletedUDPClusterIPs) != 0 { 891 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList()) 892 } 893 894 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 895 if len(healthCheckNodePorts) != 1 { 896 t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts) 897 } 898 899 // And back to ClusterIP 900 fp.updateService(servicev2, servicev1) 901 result = fp.svcPortMap.Update(fp.serviceChanges) 902 if len(fp.svcPortMap) != 2 { 903 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 904 } 905 if len(result.UpdatedServices) != 1 { 906 t.Errorf("expected 1 updated service, got %d", len(result.UpdatedServices)) 907 } 908 if len(result.DeletedUDPClusterIPs) != 0 { 909 // Services only added, so nothing stale yet 910 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 911 } 912 913 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 914 if len(healthCheckNodePorts) != 0 { 915 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 916 } 917 }