k8s.io/kubernetes@v1.29.3/pkg/proxy/nftables/proxier_test.go (about) 1 /* 2 Copyright 2015 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 nftables 18 19 import ( 20 "fmt" 21 "net" 22 "reflect" 23 "sort" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/danwinship/knftables" 29 "github.com/lithammer/dedent" 30 v1 "k8s.io/api/core/v1" 31 discovery "k8s.io/api/discovery/v1" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/types" 34 "k8s.io/apimachinery/pkg/util/intstr" 35 utilfeature "k8s.io/apiserver/pkg/util/feature" 36 featuregatetesting "k8s.io/component-base/featuregate/testing" 37 "k8s.io/component-base/metrics/testutil" 38 "k8s.io/klog/v2" 39 "k8s.io/kubernetes/pkg/features" 40 "k8s.io/kubernetes/pkg/proxy" 41 "k8s.io/kubernetes/pkg/proxy/conntrack" 42 "k8s.io/kubernetes/pkg/proxy/metrics" 43 44 "k8s.io/kubernetes/pkg/proxy/healthcheck" 45 proxyutil "k8s.io/kubernetes/pkg/proxy/util" 46 proxyutiliptables "k8s.io/kubernetes/pkg/proxy/util/iptables" 47 proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing" 48 "k8s.io/kubernetes/pkg/util/async" 49 "k8s.io/utils/exec" 50 fakeexec "k8s.io/utils/exec/testing" 51 netutils "k8s.io/utils/net" 52 "k8s.io/utils/ptr" 53 ) 54 55 func TestDeleteEndpointConnections(t *testing.T) { 56 const ( 57 UDP = v1.ProtocolUDP 58 TCP = v1.ProtocolTCP 59 SCTP = v1.ProtocolSCTP 60 ) 61 62 testCases := []struct { 63 description string 64 svcName string 65 svcIP string 66 svcPort int32 67 protocol v1.Protocol 68 endpoint string // IP:port endpoint 69 simulatedErr string 70 }{ 71 { 72 description: "V4 UDP", 73 svcName: "v4-udp", 74 svcIP: "172.30.1.1", 75 svcPort: 80, 76 protocol: UDP, 77 endpoint: "10.240.0.3:80", 78 }, 79 { 80 description: "V4 TCP", 81 svcName: "v4-tcp", 82 svcIP: "172.30.2.2", 83 svcPort: 80, 84 protocol: TCP, 85 endpoint: "10.240.0.4:80", 86 }, 87 { 88 description: "V4 SCTP", 89 svcName: "v4-sctp", 90 svcIP: "172.30.3.3", 91 svcPort: 80, 92 protocol: SCTP, 93 endpoint: "10.240.0.5:80", 94 }, 95 { 96 description: "V4 UDP, nothing to delete, benign error", 97 svcName: "v4-udp-nothing-to-delete", 98 svcIP: "172.30.4.4", 99 svcPort: 80, 100 protocol: UDP, 101 endpoint: "10.240.0.6:80", 102 simulatedErr: conntrack.NoConnectionToDelete, 103 }, 104 { 105 description: "V4 UDP, unexpected error, should be glogged", 106 svcName: "v4-udp-simulated-error", 107 svcIP: "172.30.5.5", 108 svcPort: 80, 109 protocol: UDP, 110 endpoint: "10.240.0.7:80", 111 simulatedErr: "simulated error", 112 }, 113 { 114 description: "V6 UDP", 115 svcName: "v6-udp", 116 svcIP: "fd00:1234::20", 117 svcPort: 80, 118 protocol: UDP, 119 endpoint: "[2001:db8::2]:80", 120 }, 121 { 122 description: "V6 TCP", 123 svcName: "v6-tcp", 124 svcIP: "fd00:1234::30", 125 svcPort: 80, 126 protocol: TCP, 127 endpoint: "[2001:db8::3]:80", 128 }, 129 { 130 description: "V6 SCTP", 131 svcName: "v6-sctp", 132 svcIP: "fd00:1234::40", 133 svcPort: 80, 134 protocol: SCTP, 135 endpoint: "[2001:db8::4]:80", 136 }, 137 } 138 139 for _, tc := range testCases { 140 t.Run(tc.description, func(t *testing.T) { 141 priorGlogErrs := klog.Stats.Error.Lines() 142 143 // Create a fake executor for the conntrack utility. 144 fcmd := fakeexec.FakeCmd{} 145 fexec := &fakeexec.FakeExec{ 146 LookPathFunc: func(cmd string) (string, error) { return cmd, nil }, 147 } 148 execFunc := func(cmd string, args ...string) exec.Cmd { 149 return fakeexec.InitFakeCmd(&fcmd, cmd, args...) 150 } 151 152 if tc.protocol == UDP { 153 cmdOutput := "1 flow entries have been deleted" 154 var simErr error 155 156 // First call outputs cmdOutput and succeeds 157 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, 158 func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, nil }, 159 ) 160 fexec.CommandScript = append(fexec.CommandScript, execFunc) 161 162 // Second call may succeed or fail 163 if tc.simulatedErr != "" { 164 cmdOutput = "" 165 simErr = fmt.Errorf(tc.simulatedErr) 166 } 167 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, 168 func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, simErr }, 169 ) 170 fexec.CommandScript = append(fexec.CommandScript, execFunc) 171 } 172 173 endpointIP := proxyutil.IPPart(tc.endpoint) 174 _, fp := NewFakeProxier(proxyutil.GetIPFamilyFromIP(endpointIP)) 175 fp.exec = fexec 176 177 makeServiceMap(fp, 178 makeTestService("ns1", tc.svcName, func(svc *v1.Service) { 179 svc.Spec.ClusterIP = tc.svcIP 180 svc.Spec.Ports = []v1.ServicePort{{ 181 Name: "p80", 182 Port: tc.svcPort, 183 Protocol: tc.protocol, 184 }} 185 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 186 }), 187 ) 188 fp.svcPortMap.Update(fp.serviceChanges) 189 190 slice := makeTestEndpointSlice("ns1", tc.svcName, 1, func(eps *discovery.EndpointSlice) { 191 if fp.ipFamily == v1.IPv6Protocol { 192 eps.AddressType = discovery.AddressTypeIPv6 193 } else { 194 eps.AddressType = discovery.AddressTypeIPv4 195 } 196 eps.Endpoints = []discovery.Endpoint{{ 197 Addresses: []string{endpointIP}, 198 }} 199 eps.Ports = []discovery.EndpointPort{{ 200 Name: ptr.To("p80"), 201 Port: ptr.To[int32](80), 202 Protocol: ptr.To(tc.protocol), 203 }} 204 }) 205 206 // Add and then remove the endpoint slice 207 fp.OnEndpointSliceAdd(slice) 208 fp.syncProxyRules() 209 fp.OnEndpointSliceDelete(slice) 210 fp.syncProxyRules() 211 212 // Check the executed conntrack command 213 if tc.protocol == UDP { 214 if fexec.CommandCalls != 2 { 215 t.Fatalf("Expected conntrack to be executed 2 times, but got %d", fexec.CommandCalls) 216 } 217 218 // First clear conntrack entries for the clusterIP when the 219 // endpoint is first added. 220 expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.svcIP) 221 if fp.ipFamily == v1.IPv6Protocol { 222 expectCommand += " -f ipv6" 223 } 224 actualCommand := strings.Join(fcmd.CombinedOutputLog[0], " ") 225 if actualCommand != expectCommand { 226 t.Errorf("Expected command: %s, but executed %s", expectCommand, actualCommand) 227 } 228 229 // Then clear conntrack entries for the endpoint when it is 230 // deleted. 231 expectCommand = fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.svcIP, endpointIP) 232 if fp.ipFamily == v1.IPv6Protocol { 233 expectCommand += " -f ipv6" 234 } 235 actualCommand = strings.Join(fcmd.CombinedOutputLog[1], " ") 236 if actualCommand != expectCommand { 237 t.Errorf("Expected command: %s, but executed %s", expectCommand, actualCommand) 238 } 239 } else if fexec.CommandCalls != 0 { 240 t.Fatalf("Expected conntrack to be executed 0 times, but got %d", fexec.CommandCalls) 241 } 242 243 // Check the number of new glog errors 244 var expGlogErrs int64 245 if tc.simulatedErr != "" && tc.simulatedErr != conntrack.NoConnectionToDelete { 246 expGlogErrs = 1 247 } 248 glogErrs := klog.Stats.Error.Lines() - priorGlogErrs 249 if glogErrs != expGlogErrs { 250 t.Errorf("Expected %d glogged errors, but got %d", expGlogErrs, glogErrs) 251 } 252 }) 253 } 254 } 255 256 // Conventions for tests using NewFakeProxier: 257 // 258 // Pod IPs: 10.0.0.0/8 259 // Service ClusterIPs: 172.30.0.0/16 260 // Node IPs: 192.168.0.0/24 261 // Local Node IP: 192.168.0.2 262 // Service ExternalIPs: 192.168.99.0/24 263 // LoadBalancer IPs: 1.2.3.4, 5.6.7.8, 9.10.11.12 264 // Non-cluster IPs: 203.0.113.0/24 265 // LB Source Range: 203.0.113.0/25 266 267 const testHostname = "test-hostname" 268 const testNodeIP = "192.168.0.2" 269 const testNodeIPAlt = "192.168.1.2" 270 const testExternalIP = "192.168.99.11" 271 const testNodeIPv6 = "2001:db8::1" 272 const testNodeIPv6Alt = "2001:db8:1::2" 273 const testExternalClient = "203.0.113.2" 274 const testExternalClientBlocked = "203.0.113.130" 275 276 var testNodeIPs = []string{testNodeIP, testNodeIPAlt, testExternalIP, testNodeIPv6, testNodeIPv6Alt} 277 278 func NewFakeProxier(ipFamily v1.IPFamily) (*knftables.Fake, *Proxier) { 279 // TODO: Call NewProxier after refactoring out the goroutine 280 // invocation into a Run() method. 281 nftablesFamily := knftables.IPv4Family 282 podCIDR := "10.0.0.0/8" 283 if ipFamily == v1.IPv6Protocol { 284 nftablesFamily = knftables.IPv6Family 285 podCIDR = "fd00:10::/64" 286 } 287 detectLocal, _ := proxyutiliptables.NewDetectLocalByCIDR(podCIDR) 288 289 networkInterfacer := proxyutiltest.NewFakeNetwork() 290 itf := net.Interface{Index: 0, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0} 291 addrs := []net.Addr{ 292 &net.IPNet{IP: netutils.ParseIPSloppy("127.0.0.1"), Mask: net.CIDRMask(8, 32)}, 293 &net.IPNet{IP: netutils.ParseIPSloppy("::1/128"), Mask: net.CIDRMask(128, 128)}, 294 } 295 networkInterfacer.AddInterfaceAddr(&itf, addrs) 296 itf1 := net.Interface{Index: 1, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0} 297 addrs1 := []net.Addr{ 298 &net.IPNet{IP: netutils.ParseIPSloppy(testNodeIP), Mask: net.CIDRMask(24, 32)}, 299 &net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPAlt), Mask: net.CIDRMask(24, 32)}, 300 &net.IPNet{IP: netutils.ParseIPSloppy(testExternalIP), Mask: net.CIDRMask(24, 32)}, 301 &net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPv6), Mask: net.CIDRMask(64, 128)}, 302 &net.IPNet{IP: netutils.ParseIPSloppy(testNodeIPv6Alt), Mask: net.CIDRMask(64, 128)}, 303 } 304 networkInterfacer.AddInterfaceAddr(&itf1, addrs1) 305 306 nft := knftables.NewFake(nftablesFamily, kubeProxyTable) 307 308 p := &Proxier{ 309 ipFamily: ipFamily, 310 exec: &fakeexec.FakeExec{}, 311 svcPortMap: make(proxy.ServicePortMap), 312 serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, ipFamily, nil, nil), 313 endpointsMap: make(proxy.EndpointsMap), 314 endpointsChanges: proxy.NewEndpointsChangeTracker(testHostname, newEndpointInfo, ipFamily, nil, nil), 315 nftables: nft, 316 masqueradeMark: "0x4000", 317 localDetector: detectLocal, 318 hostname: testHostname, 319 serviceHealthServer: healthcheck.NewFakeServiceHealthServer(), 320 nodeIP: netutils.ParseIPSloppy(testNodeIP), 321 nodePortAddresses: proxyutil.NewNodePortAddresses(ipFamily, nil), 322 networkInterfacer: networkInterfacer, 323 staleChains: make(map[string]time.Time), 324 } 325 p.setInitialized(true) 326 p.syncRunner = async.NewBoundedFrequencyRunner("test-sync-runner", p.syncProxyRules, 0, time.Minute, 1) 327 328 return nft, p 329 } 330 331 // TestOverallNFTablesRules creates a variety of services and verifies that the generated 332 // rules are exactly as expected. 333 func TestOverallNFTablesRules(t *testing.T) { 334 nft, fp := NewFakeProxier(v1.IPv4Protocol) 335 metrics.RegisterMetrics() 336 337 makeServiceMap(fp, 338 // create ClusterIP service 339 makeTestService("ns1", "svc1", func(svc *v1.Service) { 340 svc.Spec.ClusterIP = "172.30.0.41" 341 svc.Spec.Ports = []v1.ServicePort{{ 342 Name: "p80", 343 Port: 80, 344 Protocol: v1.ProtocolTCP, 345 }} 346 }), 347 // create LoadBalancer service with Local traffic policy 348 makeTestService("ns2", "svc2", func(svc *v1.Service) { 349 svc.Spec.Type = "LoadBalancer" 350 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 351 svc.Spec.ClusterIP = "172.30.0.42" 352 svc.Spec.Ports = []v1.ServicePort{{ 353 Name: "p80", 354 Port: 80, 355 Protocol: v1.ProtocolTCP, 356 NodePort: 3001, 357 }} 358 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 359 IP: "1.2.3.4", 360 }} 361 svc.Spec.ExternalIPs = []string{"192.168.99.22"} 362 svc.Spec.HealthCheckNodePort = 30000 363 }), 364 // create NodePort service 365 makeTestService("ns3", "svc3", func(svc *v1.Service) { 366 svc.Spec.Type = "NodePort" 367 svc.Spec.ClusterIP = "172.30.0.43" 368 svc.Spec.Ports = []v1.ServicePort{{ 369 Name: "p80", 370 Port: 80, 371 Protocol: v1.ProtocolTCP, 372 NodePort: 3003, 373 }} 374 }), 375 // create ExternalIP service 376 makeTestService("ns4", "svc4", func(svc *v1.Service) { 377 svc.Spec.Type = "NodePort" 378 svc.Spec.ClusterIP = "172.30.0.44" 379 svc.Spec.ExternalIPs = []string{"192.168.99.33"} 380 svc.Spec.Ports = []v1.ServicePort{{ 381 Name: "p80", 382 Port: 80, 383 Protocol: v1.ProtocolTCP, 384 TargetPort: intstr.FromInt32(80), 385 }} 386 }), 387 // create LoadBalancer service with Cluster traffic policy, source ranges, 388 // and session affinity 389 makeTestService("ns5", "svc5", func(svc *v1.Service) { 390 svc.Spec.Type = "LoadBalancer" 391 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyCluster 392 svc.Spec.ClusterIP = "172.30.0.45" 393 svc.Spec.Ports = []v1.ServicePort{{ 394 Name: "p80", 395 Port: 80, 396 Protocol: v1.ProtocolTCP, 397 NodePort: 3002, 398 }} 399 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 400 IP: "5.6.7.8", 401 }} 402 svc.Spec.HealthCheckNodePort = 30000 403 // Extra whitespace to ensure that invalid value will not result 404 // in a crash, for backward compatibility. 405 svc.Spec.LoadBalancerSourceRanges = []string{" 203.0.113.0/25"} 406 407 svc.Spec.SessionAffinity = v1.ServiceAffinityClientIP 408 svc.Spec.SessionAffinityConfig = &v1.SessionAffinityConfig{ 409 ClientIP: &v1.ClientIPConfig{ 410 TimeoutSeconds: ptr.To[int32](10800), 411 }, 412 } 413 }), 414 // create ClusterIP service with no endpoints 415 makeTestService("ns6", "svc6", func(svc *v1.Service) { 416 svc.Spec.Type = "ClusterIP" 417 svc.Spec.ClusterIP = "172.30.0.46" 418 svc.Spec.Ports = []v1.ServicePort{{ 419 Name: "p80", 420 Port: 80, 421 Protocol: v1.ProtocolTCP, 422 TargetPort: intstr.FromInt32(80), 423 }} 424 }), 425 ) 426 populateEndpointSlices(fp, 427 // create ClusterIP service endpoints 428 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 429 eps.AddressType = discovery.AddressTypeIPv4 430 eps.Endpoints = []discovery.Endpoint{{ 431 Addresses: []string{"10.180.0.1"}, 432 }} 433 eps.Ports = []discovery.EndpointPort{{ 434 Name: ptr.To("p80"), 435 Port: ptr.To[int32](80), 436 Protocol: ptr.To(v1.ProtocolTCP), 437 }} 438 }), 439 // create Local LoadBalancer endpoints. Note that since we aren't setting 440 // its NodeName, this endpoint will be considered non-local and ignored. 441 makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) { 442 eps.AddressType = discovery.AddressTypeIPv4 443 eps.Endpoints = []discovery.Endpoint{{ 444 Addresses: []string{"10.180.0.2"}, 445 }} 446 eps.Ports = []discovery.EndpointPort{{ 447 Name: ptr.To("p80"), 448 Port: ptr.To[int32](80), 449 Protocol: ptr.To(v1.ProtocolTCP), 450 }} 451 }), 452 // create NodePort service endpoints 453 makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) { 454 eps.AddressType = discovery.AddressTypeIPv4 455 eps.Endpoints = []discovery.Endpoint{{ 456 Addresses: []string{"10.180.0.3"}, 457 }} 458 eps.Ports = []discovery.EndpointPort{{ 459 Name: ptr.To("p80"), 460 Port: ptr.To[int32](80), 461 Protocol: ptr.To(v1.ProtocolTCP), 462 }} 463 }), 464 // create ExternalIP service endpoints 465 makeTestEndpointSlice("ns4", "svc4", 1, func(eps *discovery.EndpointSlice) { 466 eps.AddressType = discovery.AddressTypeIPv4 467 eps.Endpoints = []discovery.Endpoint{{ 468 Addresses: []string{"10.180.0.4"}, 469 }, { 470 Addresses: []string{"10.180.0.5"}, 471 NodeName: ptr.To(testHostname), 472 }} 473 eps.Ports = []discovery.EndpointPort{{ 474 Name: ptr.To("p80"), 475 Port: ptr.To[int32](80), 476 Protocol: ptr.To(v1.ProtocolTCP), 477 }} 478 }), 479 // create Cluster LoadBalancer endpoints 480 makeTestEndpointSlice("ns5", "svc5", 1, func(eps *discovery.EndpointSlice) { 481 eps.AddressType = discovery.AddressTypeIPv4 482 eps.Endpoints = []discovery.Endpoint{{ 483 Addresses: []string{"10.180.0.3"}, 484 }} 485 eps.Ports = []discovery.EndpointPort{{ 486 Name: ptr.To("p80"), 487 Port: ptr.To[int32](80), 488 Protocol: ptr.To(v1.ProtocolTCP), 489 }} 490 }), 491 ) 492 493 fp.syncProxyRules() 494 495 expected := dedent.Dedent(` 496 add table ip kube-proxy { comment "rules for kube-proxy" ; } 497 498 add chain ip kube-proxy forward 499 add rule ip kube-proxy forward ct state invalid drop 500 add chain ip kube-proxy mark-for-masquerade 501 add rule ip kube-proxy mark-for-masquerade mark set mark or 0x4000 502 add chain ip kube-proxy masquerading 503 add rule ip kube-proxy masquerading mark and 0x4000 == 0 return 504 add rule ip kube-proxy masquerading mark set mark xor 0x4000 505 add rule ip kube-proxy masquerading masquerade fully-random 506 add chain ip kube-proxy services 507 add chain ip kube-proxy filter-forward { type filter hook forward priority -101 ; } 508 add rule ip kube-proxy filter-forward ct state new jump endpoints-check 509 add rule ip kube-proxy filter-forward jump forward 510 add rule ip kube-proxy filter-forward ct state new jump firewall-check 511 add chain ip kube-proxy filter-input { type filter hook input priority -101 ; } 512 add rule ip kube-proxy filter-input ct state new jump endpoints-check 513 add rule ip kube-proxy filter-input ct state new jump firewall-check 514 add chain ip kube-proxy filter-output { type filter hook output priority -101 ; } 515 add rule ip kube-proxy filter-output ct state new jump endpoints-check 516 add rule ip kube-proxy filter-output ct state new jump firewall-check 517 add chain ip kube-proxy nat-output { type nat hook output priority -100 ; } 518 add rule ip kube-proxy nat-output jump services 519 add chain ip kube-proxy nat-postrouting { type nat hook postrouting priority 100 ; } 520 add rule ip kube-proxy nat-postrouting jump masquerading 521 add chain ip kube-proxy nat-prerouting { type nat hook prerouting priority -100 ; } 522 add rule ip kube-proxy nat-prerouting jump services 523 524 add set ip kube-proxy firewall { type ipv4_addr . inet_proto . inet_service ; comment "destinations that are subject to LoadBalancerSourceRanges" ; } 525 add set ip kube-proxy firewall-allow { type ipv4_addr . inet_proto . inet_service . ipv4_addr ; flags interval ; comment "destinations+sources that are allowed by LoadBalancerSourceRanges" ; } 526 add chain ip kube-proxy firewall-check 527 add chain ip kube-proxy firewall-allow-check 528 add rule ip kube-proxy firewall-allow-check ip daddr . meta l4proto . th dport . ip saddr @firewall-allow return 529 add rule ip kube-proxy firewall-allow-check drop 530 add rule ip kube-proxy firewall-check ip daddr . meta l4proto . th dport @firewall jump firewall-allow-check 531 532 add chain ip kube-proxy reject-chain { comment "helper for @no-endpoint-services / @no-endpoint-nodeports" ; } 533 add rule ip kube-proxy reject-chain reject 534 535 add map ip kube-proxy no-endpoint-services { type ipv4_addr . inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to services with no endpoints" ; } 536 add map ip kube-proxy no-endpoint-nodeports { type inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to service nodeports with no endpoints" ; } 537 538 add chain ip kube-proxy endpoints-check 539 add rule ip kube-proxy endpoints-check ip daddr . meta l4proto . th dport vmap @no-endpoint-services 540 add rule ip kube-proxy endpoints-check fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @no-endpoint-nodeports 541 542 add map ip kube-proxy service-ips { type ipv4_addr . inet_proto . inet_service : verdict ; comment "ClusterIP, ExternalIP and LoadBalancer IP traffic" ; } 543 add map ip kube-proxy service-nodeports { type inet_proto . inet_service : verdict ; comment "NodePort traffic" ; } 544 add rule ip kube-proxy services ip daddr . meta l4proto . th dport vmap @service-ips 545 add rule ip kube-proxy services fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @service-nodeports 546 547 # svc1 548 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 549 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 550 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 } 551 552 add chain ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 553 add rule ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 ip saddr 10.180.0.1 jump mark-for-masquerade 554 add rule ip kube-proxy endpoint-5OJB2KTY-ns1/svc1/tcp/p80__10.180.0.1/80 meta l4proto tcp dnat to 10.180.0.1:80 555 556 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 557 558 # svc2 559 add chain ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80 560 add rule ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80 ip daddr 172.30.0.42 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 561 add rule ip kube-proxy service-42NFTM6N-ns2/svc2/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 } 562 add chain ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 563 add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 ip saddr 10.0.0.0/8 goto service-42NFTM6N-ns2/svc2/tcp/p80 comment "short-circuit pod traffic" 564 add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 fib saddr type local jump mark-for-masquerade comment "masquerade local traffic" 565 add rule ip kube-proxy external-42NFTM6N-ns2/svc2/tcp/p80 fib saddr type local goto service-42NFTM6N-ns2/svc2/tcp/p80 comment "short-circuit local traffic" 566 add chain ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 567 add rule ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 ip saddr 10.180.0.2 jump mark-for-masquerade 568 add rule ip kube-proxy endpoint-SGOXE6O3-ns2/svc2/tcp/p80__10.180.0.2/80 meta l4proto tcp dnat to 10.180.0.2:80 569 570 add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 80 : goto service-42NFTM6N-ns2/svc2/tcp/p80 } 571 add element ip kube-proxy service-ips { 192.168.99.22 . tcp . 80 : goto external-42NFTM6N-ns2/svc2/tcp/p80 } 572 add element ip kube-proxy service-ips { 1.2.3.4 . tcp . 80 : goto external-42NFTM6N-ns2/svc2/tcp/p80 } 573 add element ip kube-proxy service-nodeports { tcp . 3001 : goto external-42NFTM6N-ns2/svc2/tcp/p80 } 574 575 add element ip kube-proxy no-endpoint-nodeports { tcp . 3001 comment "ns2/svc2:p80" : drop } 576 add element ip kube-proxy no-endpoint-services { 1.2.3.4 . tcp . 80 comment "ns2/svc2:p80" : drop } 577 add element ip kube-proxy no-endpoint-services { 192.168.99.22 . tcp . 80 comment "ns2/svc2:p80" : drop } 578 579 # svc3 580 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 581 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 582 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 } 583 add chain ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80 584 add rule ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80 jump mark-for-masquerade 585 add rule ip kube-proxy external-4AT6LBPK-ns3/svc3/tcp/p80 goto service-4AT6LBPK-ns3/svc3/tcp/p80 586 add chain ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 587 add rule ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 ip saddr 10.180.0.3 jump mark-for-masquerade 588 add rule ip kube-proxy endpoint-UEIP74TE-ns3/svc3/tcp/p80__10.180.0.3/80 meta l4proto tcp dnat to 10.180.0.3:80 589 590 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 591 add element ip kube-proxy service-nodeports { tcp . 3003 : goto external-4AT6LBPK-ns3/svc3/tcp/p80 } 592 593 # svc4 594 add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 595 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 596 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 2 vmap { 0 : goto endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 , 1 : goto endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 } 597 add chain ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80 598 add rule ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80 jump mark-for-masquerade 599 add rule ip kube-proxy external-LAUZTJTB-ns4/svc4/tcp/p80 goto service-LAUZTJTB-ns4/svc4/tcp/p80 600 add chain ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 601 add rule ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 ip saddr 10.180.0.5 jump mark-for-masquerade 602 add rule ip kube-proxy endpoint-5RFCDDV7-ns4/svc4/tcp/p80__10.180.0.5/80 meta l4proto tcp dnat to 10.180.0.5:80 603 add chain ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 604 add rule ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 ip saddr 10.180.0.4 jump mark-for-masquerade 605 add rule ip kube-proxy endpoint-UNZV3OEC-ns4/svc4/tcp/p80__10.180.0.4/80 meta l4proto tcp dnat to 10.180.0.4:80 606 607 add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 } 608 add element ip kube-proxy service-ips { 192.168.99.33 . tcp . 80 : goto external-LAUZTJTB-ns4/svc4/tcp/p80 } 609 610 # svc5 611 add set ip kube-proxy affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 { type ipv4_addr ; flags dynamic,timeout ; timeout 10800s ; } 612 add chain ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 613 add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 ip daddr 172.30.0.45 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 614 add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 ip saddr @affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 goto endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 615 add rule ip kube-proxy service-HVFWP5L3-ns5/svc5/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 } 616 add chain ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80 617 add rule ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80 jump mark-for-masquerade 618 add rule ip kube-proxy external-HVFWP5L3-ns5/svc5/tcp/p80 goto service-HVFWP5L3-ns5/svc5/tcp/p80 619 620 add chain ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 621 add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 ip saddr 10.180.0.3 jump mark-for-masquerade 622 add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 update @affinity-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 { ip saddr } 623 add rule ip kube-proxy endpoint-GTK6MW7G-ns5/svc5/tcp/p80__10.180.0.3/80 meta l4proto tcp dnat to 10.180.0.3:80 624 625 add element ip kube-proxy service-ips { 172.30.0.45 . tcp . 80 : goto service-HVFWP5L3-ns5/svc5/tcp/p80 } 626 add element ip kube-proxy service-ips { 5.6.7.8 . tcp . 80 : goto external-HVFWP5L3-ns5/svc5/tcp/p80 } 627 add element ip kube-proxy service-nodeports { tcp . 3002 : goto external-HVFWP5L3-ns5/svc5/tcp/p80 } 628 add element ip kube-proxy firewall { 5.6.7.8 . tcp . 80 comment "ns5/svc5:p80" } 629 add element ip kube-proxy firewall-allow { 5.6.7.8 . tcp . 80 . 203.0.113.0/25 comment "ns5/svc5:p80" } 630 631 # svc6 632 add element ip kube-proxy no-endpoint-services { 172.30.0.46 . tcp . 80 comment "ns6/svc6:p80" : goto reject-chain } 633 `) 634 635 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 636 } 637 638 // TestNoEndpointsReject tests that a service with no endpoints rejects connections to 639 // its ClusterIP, ExternalIPs, NodePort, and LoadBalancer IP. 640 func TestNoEndpointsReject(t *testing.T) { 641 nft, fp := NewFakeProxier(v1.IPv4Protocol) 642 svcIP := "172.30.0.41" 643 svcPort := 80 644 svcNodePort := 3001 645 svcExternalIPs := "192.168.99.11" 646 svcLBIP := "1.2.3.4" 647 svcPortName := proxy.ServicePortName{ 648 NamespacedName: makeNSN("ns1", "svc1"), 649 Port: "p80", 650 } 651 652 makeServiceMap(fp, 653 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 654 svc.Spec.Type = v1.ServiceTypeLoadBalancer 655 svc.Spec.ClusterIP = svcIP 656 svc.Spec.ExternalIPs = []string{svcExternalIPs} 657 svc.Spec.Ports = []v1.ServicePort{{ 658 Name: svcPortName.Port, 659 Protocol: v1.ProtocolTCP, 660 Port: int32(svcPort), 661 NodePort: int32(svcNodePort), 662 }} 663 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 664 IP: svcLBIP, 665 }} 666 }), 667 ) 668 fp.syncProxyRules() 669 670 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 671 { 672 name: "pod to cluster IP with no endpoints", 673 sourceIP: "10.0.0.2", 674 destIP: svcIP, 675 destPort: svcPort, 676 output: "REJECT", 677 }, 678 { 679 name: "external to external IP with no endpoints", 680 sourceIP: testExternalClient, 681 destIP: svcExternalIPs, 682 destPort: svcPort, 683 output: "REJECT", 684 }, 685 { 686 name: "pod to NodePort with no endpoints", 687 sourceIP: "10.0.0.2", 688 destIP: testNodeIP, 689 destPort: svcNodePort, 690 output: "REJECT", 691 }, 692 { 693 name: "external to NodePort with no endpoints", 694 sourceIP: testExternalClient, 695 destIP: testNodeIP, 696 destPort: svcNodePort, 697 output: "REJECT", 698 }, 699 { 700 name: "pod to LoadBalancer IP with no endpoints", 701 sourceIP: "10.0.0.2", 702 destIP: svcLBIP, 703 destPort: svcPort, 704 output: "REJECT", 705 }, 706 { 707 name: "external to LoadBalancer IP with no endpoints", 708 sourceIP: testExternalClient, 709 destIP: svcLBIP, 710 destPort: svcPort, 711 output: "REJECT", 712 }, 713 }) 714 } 715 716 // TestClusterIPGeneral tests various basic features of a ClusterIP service 717 func TestClusterIPGeneral(t *testing.T) { 718 nft, fp := NewFakeProxier(v1.IPv4Protocol) 719 720 makeServiceMap(fp, 721 makeTestService("ns1", "svc1", func(svc *v1.Service) { 722 svc.Spec.ClusterIP = "172.30.0.41" 723 svc.Spec.Ports = []v1.ServicePort{{ 724 Name: "http", 725 Port: 80, 726 Protocol: v1.ProtocolTCP, 727 }} 728 }), 729 makeTestService("ns2", "svc2", func(svc *v1.Service) { 730 svc.Spec.ClusterIP = "172.30.0.42" 731 svc.Spec.Ports = []v1.ServicePort{ 732 { 733 Name: "http", 734 Port: 80, 735 Protocol: v1.ProtocolTCP, 736 }, 737 { 738 Name: "https", 739 Port: 443, 740 Protocol: v1.ProtocolTCP, 741 TargetPort: intstr.FromInt32(8443), 742 }, 743 { 744 // Of course this should really be UDP, but if we 745 // create a service with UDP ports, the Proxier will 746 // try to do conntrack cleanup and we'd have to set 747 // the FakeExec up to be able to deal with that... 748 Name: "dns-sctp", 749 Port: 53, 750 Protocol: v1.ProtocolSCTP, 751 }, 752 { 753 Name: "dns-tcp", 754 Port: 53, 755 Protocol: v1.ProtocolTCP, 756 // We use TargetPort on TCP but not SCTP to help 757 // disambiguate the output. 758 TargetPort: intstr.FromInt32(5353), 759 }, 760 } 761 }), 762 ) 763 764 populateEndpointSlices(fp, 765 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 766 eps.AddressType = discovery.AddressTypeIPv4 767 eps.Endpoints = []discovery.Endpoint{{ 768 Addresses: []string{"10.180.0.1"}, 769 NodeName: ptr.To(testHostname), 770 }} 771 eps.Ports = []discovery.EndpointPort{{ 772 Name: ptr.To("http"), 773 Port: ptr.To[int32](80), 774 Protocol: ptr.To(v1.ProtocolTCP), 775 }} 776 }), 777 makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) { 778 eps.AddressType = discovery.AddressTypeIPv4 779 eps.Endpoints = []discovery.Endpoint{ 780 { 781 Addresses: []string{"10.180.0.1"}, 782 NodeName: ptr.To(testHostname), 783 }, 784 { 785 Addresses: []string{"10.180.2.1"}, 786 NodeName: ptr.To("host2"), 787 }, 788 } 789 eps.Ports = []discovery.EndpointPort{ 790 { 791 Name: ptr.To("http"), 792 Port: ptr.To[int32](80), 793 Protocol: ptr.To(v1.ProtocolTCP), 794 }, 795 { 796 Name: ptr.To("https"), 797 Port: ptr.To[int32](8443), 798 Protocol: ptr.To(v1.ProtocolTCP), 799 }, 800 { 801 Name: ptr.To("dns-sctp"), 802 Port: ptr.To[int32](53), 803 Protocol: ptr.To(v1.ProtocolSCTP), 804 }, 805 { 806 Name: ptr.To("dns-tcp"), 807 Port: ptr.To[int32](5353), 808 Protocol: ptr.To(v1.ProtocolTCP), 809 }, 810 } 811 }), 812 ) 813 814 fp.syncProxyRules() 815 816 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 817 { 818 name: "simple clusterIP", 819 sourceIP: "10.180.0.2", 820 destIP: "172.30.0.41", 821 destPort: 80, 822 output: "10.180.0.1:80", 823 masq: false, 824 }, 825 { 826 name: "hairpin to cluster IP", 827 sourceIP: "10.180.0.1", 828 destIP: "172.30.0.41", 829 destPort: 80, 830 output: "10.180.0.1:80", 831 masq: true, 832 }, 833 { 834 name: "clusterIP with multiple endpoints", 835 sourceIP: "10.180.0.2", 836 destIP: "172.30.0.42", 837 destPort: 80, 838 output: "10.180.0.1:80, 10.180.2.1:80", 839 masq: false, 840 }, 841 { 842 name: "clusterIP with TargetPort", 843 sourceIP: "10.180.0.2", 844 destIP: "172.30.0.42", 845 destPort: 443, 846 output: "10.180.0.1:8443, 10.180.2.1:8443", 847 masq: false, 848 }, 849 { 850 name: "clusterIP with TCP and SCTP on same port (TCP)", 851 sourceIP: "10.180.0.2", 852 protocol: v1.ProtocolTCP, 853 destIP: "172.30.0.42", 854 destPort: 53, 855 output: "10.180.0.1:5353, 10.180.2.1:5353", 856 masq: false, 857 }, 858 { 859 name: "clusterIP with TCP and SCTP on same port (SCTP)", 860 sourceIP: "10.180.0.2", 861 protocol: v1.ProtocolSCTP, 862 destIP: "172.30.0.42", 863 destPort: 53, 864 output: "10.180.0.1:53, 10.180.2.1:53", 865 masq: false, 866 }, 867 { 868 name: "TCP-only port does not match UDP traffic", 869 sourceIP: "10.180.0.2", 870 protocol: v1.ProtocolUDP, 871 destIP: "172.30.0.42", 872 destPort: 80, 873 output: "", 874 }, 875 { 876 name: "svc1 does not accept svc2's ports", 877 sourceIP: "10.180.0.2", 878 destIP: "172.30.0.41", 879 destPort: 443, 880 output: "", 881 }, 882 }) 883 } 884 885 func TestLoadBalancer(t *testing.T) { 886 nft, fp := NewFakeProxier(v1.IPv4Protocol) 887 svcIP := "172.30.0.41" 888 svcPort := 80 889 svcNodePort := 3001 890 svcLBIP1 := "1.2.3.4" 891 svcLBIP2 := "5.6.7.8" 892 svcPortName := proxy.ServicePortName{ 893 NamespacedName: makeNSN("ns1", "svc1"), 894 Port: "p80", 895 Protocol: v1.ProtocolTCP, 896 } 897 898 makeServiceMap(fp, 899 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 900 svc.Spec.Type = "LoadBalancer" 901 svc.Spec.ClusterIP = svcIP 902 svc.Spec.Ports = []v1.ServicePort{{ 903 Name: svcPortName.Port, 904 Port: int32(svcPort), 905 Protocol: v1.ProtocolTCP, 906 NodePort: int32(svcNodePort), 907 }} 908 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{ 909 {IP: svcLBIP1}, 910 {IP: svcLBIP2}, 911 } 912 svc.Spec.LoadBalancerSourceRanges = []string{ 913 "192.168.0.0/24", 914 915 // Regression test that excess whitespace gets ignored 916 " 203.0.113.0/25", 917 } 918 }), 919 ) 920 921 epIP := "10.180.0.1" 922 populateEndpointSlices(fp, 923 makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) { 924 eps.AddressType = discovery.AddressTypeIPv4 925 eps.Endpoints = []discovery.Endpoint{{ 926 Addresses: []string{epIP}, 927 }} 928 eps.Ports = []discovery.EndpointPort{{ 929 Name: ptr.To(svcPortName.Port), 930 Port: ptr.To(int32(svcPort)), 931 Protocol: ptr.To(v1.ProtocolTCP), 932 }} 933 }), 934 ) 935 936 fp.syncProxyRules() 937 938 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 939 { 940 name: "pod to cluster IP", 941 sourceIP: "10.0.0.2", 942 destIP: svcIP, 943 destPort: svcPort, 944 output: fmt.Sprintf("%s:%d", epIP, svcPort), 945 masq: false, 946 }, 947 { 948 name: "external to nodePort", 949 sourceIP: testExternalClient, 950 destIP: testNodeIP, 951 destPort: svcNodePort, 952 output: fmt.Sprintf("%s:%d", epIP, svcPort), 953 masq: true, 954 }, 955 { 956 name: "nodePort bypasses LoadBalancerSourceRanges", 957 sourceIP: testExternalClientBlocked, 958 destIP: testNodeIP, 959 destPort: svcNodePort, 960 output: fmt.Sprintf("%s:%d", epIP, svcPort), 961 masq: true, 962 }, 963 { 964 name: "accepted external to LB1", 965 sourceIP: testExternalClient, 966 destIP: svcLBIP1, 967 destPort: svcPort, 968 output: fmt.Sprintf("%s:%d", epIP, svcPort), 969 masq: true, 970 }, 971 { 972 name: "accepted external to LB2", 973 sourceIP: testExternalClient, 974 destIP: svcLBIP2, 975 destPort: svcPort, 976 output: fmt.Sprintf("%s:%d", epIP, svcPort), 977 masq: true, 978 }, 979 { 980 name: "blocked external to LB1", 981 sourceIP: testExternalClientBlocked, 982 destIP: svcLBIP1, 983 destPort: svcPort, 984 output: "DROP", 985 }, 986 { 987 name: "blocked external to LB2", 988 sourceIP: testExternalClientBlocked, 989 destIP: svcLBIP2, 990 destPort: svcPort, 991 output: "DROP", 992 }, 993 { 994 name: "pod to LB1 (blocked by LoadBalancerSourceRanges)", 995 sourceIP: "10.0.0.2", 996 destIP: svcLBIP1, 997 destPort: svcPort, 998 output: "DROP", 999 }, 1000 { 1001 name: "pod to LB2 (blocked by LoadBalancerSourceRanges)", 1002 sourceIP: "10.0.0.2", 1003 destIP: svcLBIP2, 1004 destPort: svcPort, 1005 output: "DROP", 1006 }, 1007 { 1008 name: "node to LB1 (allowed by LoadBalancerSourceRanges)", 1009 sourceIP: testNodeIP, 1010 destIP: svcLBIP1, 1011 destPort: svcPort, 1012 output: fmt.Sprintf("%s:%d", epIP, svcPort), 1013 masq: true, 1014 }, 1015 { 1016 name: "node to LB2 (allowed by LoadBalancerSourceRanges)", 1017 sourceIP: testNodeIP, 1018 destIP: svcLBIP2, 1019 destPort: svcPort, 1020 output: fmt.Sprintf("%s:%d", epIP, svcPort), 1021 masq: true, 1022 }, 1023 1024 // The LB rules assume that when you connect from a node to a LB IP, that 1025 // something external to kube-proxy will cause the connection to be 1026 // SNATted to the LB IP, so if the LoadBalancerSourceRanges include the 1027 // node IP, then we add a rule allowing traffic from the LB IP as well... 1028 { 1029 name: "same node to LB1, SNATted to LB1 (implicitly allowed)", 1030 sourceIP: svcLBIP1, 1031 destIP: svcLBIP1, 1032 destPort: svcPort, 1033 output: fmt.Sprintf("%s:%d", epIP, svcPort), 1034 masq: true, 1035 }, 1036 { 1037 name: "same node to LB2, SNATted to LB2 (implicitly allowed)", 1038 sourceIP: svcLBIP2, 1039 destIP: svcLBIP2, 1040 destPort: svcPort, 1041 output: fmt.Sprintf("%s:%d", epIP, svcPort), 1042 masq: true, 1043 }, 1044 }) 1045 } 1046 1047 // TestNodePorts tests NodePort services under various combinations of the 1048 // --nodeport-addresses and --localhost-nodeports flags. 1049 func TestNodePorts(t *testing.T) { 1050 testCases := []struct { 1051 name string 1052 1053 family v1.IPFamily 1054 nodePortAddresses []string 1055 1056 // allowAltNodeIP is true if we expect NodePort traffic on the alternate 1057 // node IP to be accepted 1058 allowAltNodeIP bool 1059 1060 // expectFirewall is true if we expect firewall to be filled in with 1061 // an anti-martian-packet rule 1062 expectFirewall bool 1063 }{ 1064 { 1065 name: "ipv4", 1066 1067 family: v1.IPv4Protocol, 1068 nodePortAddresses: nil, 1069 1070 allowAltNodeIP: true, 1071 expectFirewall: true, 1072 }, 1073 { 1074 name: "ipv4, multiple nodeport-addresses", 1075 1076 family: v1.IPv4Protocol, 1077 nodePortAddresses: []string{"192.168.0.0/24", "192.168.1.0/24", "2001:db8::/64"}, 1078 1079 allowAltNodeIP: true, 1080 expectFirewall: false, 1081 }, 1082 { 1083 name: "ipv6", 1084 1085 family: v1.IPv6Protocol, 1086 nodePortAddresses: nil, 1087 1088 allowAltNodeIP: true, 1089 expectFirewall: false, 1090 }, 1091 { 1092 name: "ipv6, multiple nodeport-addresses", 1093 1094 family: v1.IPv6Protocol, 1095 nodePortAddresses: []string{"192.168.0.0/24", "192.168.1.0/24", "2001:db8::/64"}, 1096 1097 allowAltNodeIP: false, 1098 expectFirewall: false, 1099 }, 1100 } 1101 1102 for _, tc := range testCases { 1103 t.Run(tc.name, func(t *testing.T) { 1104 nft, fp := NewFakeProxier(tc.family) 1105 1106 var svcIP, epIP1, epIP2 string 1107 if tc.family == v1.IPv4Protocol { 1108 svcIP = "172.30.0.41" 1109 epIP1 = "10.180.0.1" 1110 epIP2 = "10.180.2.1" 1111 } else { 1112 svcIP = "fd00:172:30::41" 1113 epIP1 = "fd00:10:180::1" 1114 epIP2 = "fd00:10:180::2:1" 1115 } 1116 if tc.nodePortAddresses != nil { 1117 fp.nodePortAddresses = proxyutil.NewNodePortAddresses(tc.family, tc.nodePortAddresses) 1118 } 1119 1120 makeServiceMap(fp, 1121 makeTestService("ns1", "svc1", func(svc *v1.Service) { 1122 svc.Spec.Type = v1.ServiceTypeNodePort 1123 svc.Spec.ClusterIP = svcIP 1124 svc.Spec.Ports = []v1.ServicePort{{ 1125 Name: "p80", 1126 Port: 80, 1127 Protocol: v1.ProtocolTCP, 1128 NodePort: 3001, 1129 }} 1130 }), 1131 ) 1132 1133 populateEndpointSlices(fp, 1134 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 1135 if tc.family == v1.IPv4Protocol { 1136 eps.AddressType = discovery.AddressTypeIPv4 1137 } else { 1138 eps.AddressType = discovery.AddressTypeIPv6 1139 } 1140 eps.Endpoints = []discovery.Endpoint{{ 1141 Addresses: []string{epIP1}, 1142 NodeName: nil, 1143 }, { 1144 Addresses: []string{epIP2}, 1145 NodeName: ptr.To(testHostname), 1146 }} 1147 eps.Ports = []discovery.EndpointPort{{ 1148 Name: ptr.To("p80"), 1149 Port: ptr.To[int32](80), 1150 Protocol: ptr.To(v1.ProtocolTCP), 1151 }} 1152 }), 1153 ) 1154 1155 fp.syncProxyRules() 1156 1157 var podIP, externalClientIP, nodeIP, altNodeIP string 1158 if tc.family == v1.IPv4Protocol { 1159 podIP = "10.0.0.2" 1160 externalClientIP = testExternalClient 1161 nodeIP = testNodeIP 1162 altNodeIP = testNodeIPAlt 1163 } else { 1164 podIP = "fd00:10::2" 1165 externalClientIP = "2600:5200::1" 1166 nodeIP = testNodeIPv6 1167 altNodeIP = testNodeIPv6Alt 1168 } 1169 output := net.JoinHostPort(epIP1, "80") + ", " + net.JoinHostPort(epIP2, "80") 1170 1171 // Basic tests are the same for all cases 1172 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 1173 { 1174 name: "pod to cluster IP", 1175 sourceIP: podIP, 1176 destIP: svcIP, 1177 destPort: 80, 1178 output: output, 1179 masq: false, 1180 }, 1181 { 1182 name: "external to nodePort", 1183 sourceIP: externalClientIP, 1184 destIP: nodeIP, 1185 destPort: 3001, 1186 output: output, 1187 masq: true, 1188 }, 1189 { 1190 name: "node to nodePort", 1191 sourceIP: nodeIP, 1192 destIP: nodeIP, 1193 destPort: 3001, 1194 output: output, 1195 masq: true, 1196 }, 1197 }) 1198 1199 // NodePort on altNodeIP should be allowed, unless 1200 // nodePortAddressess excludes altNodeIP 1201 if tc.allowAltNodeIP { 1202 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 1203 { 1204 name: "external to nodePort on secondary IP", 1205 sourceIP: externalClientIP, 1206 destIP: altNodeIP, 1207 destPort: 3001, 1208 output: output, 1209 masq: true, 1210 }, 1211 }) 1212 } else { 1213 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 1214 { 1215 name: "secondary nodeIP ignores NodePorts", 1216 sourceIP: externalClientIP, 1217 destIP: altNodeIP, 1218 destPort: 3001, 1219 output: "", 1220 }, 1221 }) 1222 } 1223 }) 1224 } 1225 } 1226 1227 func TestDropInvalidRule(t *testing.T) { 1228 for _, tcpLiberal := range []bool{false, true} { 1229 t.Run(fmt.Sprintf("tcpLiberal %t", tcpLiberal), func(t *testing.T) { 1230 nft, fp := NewFakeProxier(v1.IPv4Protocol) 1231 fp.conntrackTCPLiberal = tcpLiberal 1232 fp.syncProxyRules() 1233 1234 var expected string 1235 if !tcpLiberal { 1236 expected = "ct state invalid drop" 1237 } 1238 1239 assertNFTablesChainEqual(t, getLine(), nft, kubeForwardChain, expected) 1240 }) 1241 } 1242 } 1243 1244 // TestExternalTrafficPolicyLocal tests that traffic to externally-facing IPs does not get 1245 // masqueraded when using Local traffic policy. For traffic from external sources, that 1246 // means it can also only be routed to local endpoints, but for traffic from internal 1247 // sources, it gets routed to all endpoints. 1248 func TestExternalTrafficPolicyLocal(t *testing.T) { 1249 nft, fp := NewFakeProxier(v1.IPv4Protocol) 1250 1251 svcIP := "172.30.0.41" 1252 svcPort := 80 1253 svcNodePort := 3001 1254 svcHealthCheckNodePort := 30000 1255 svcExternalIPs := "192.168.99.11" 1256 svcLBIP := "1.2.3.4" 1257 svcPortName := proxy.ServicePortName{ 1258 NamespacedName: makeNSN("ns1", "svc1"), 1259 Port: "p80", 1260 } 1261 1262 makeServiceMap(fp, 1263 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 1264 svc.Spec.Type = v1.ServiceTypeLoadBalancer 1265 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 1266 svc.Spec.ClusterIP = svcIP 1267 svc.Spec.ExternalIPs = []string{svcExternalIPs} 1268 svc.Spec.Ports = []v1.ServicePort{{ 1269 Name: svcPortName.Port, 1270 Port: int32(svcPort), 1271 Protocol: v1.ProtocolTCP, 1272 NodePort: int32(svcNodePort), 1273 TargetPort: intstr.FromInt32(int32(svcPort)), 1274 }} 1275 svc.Spec.HealthCheckNodePort = int32(svcHealthCheckNodePort) 1276 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 1277 IP: svcLBIP, 1278 }} 1279 }), 1280 ) 1281 1282 epIP1 := "10.180.0.1" 1283 epIP2 := "10.180.2.1" 1284 populateEndpointSlices(fp, 1285 makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) { 1286 eps.AddressType = discovery.AddressTypeIPv4 1287 eps.Endpoints = []discovery.Endpoint{{ 1288 Addresses: []string{epIP1}, 1289 }, { 1290 Addresses: []string{epIP2}, 1291 NodeName: ptr.To(testHostname), 1292 }} 1293 eps.Ports = []discovery.EndpointPort{{ 1294 Name: ptr.To(svcPortName.Port), 1295 Port: ptr.To(int32(svcPort)), 1296 Protocol: ptr.To(v1.ProtocolTCP), 1297 }} 1298 }), 1299 ) 1300 1301 fp.syncProxyRules() 1302 1303 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 1304 { 1305 name: "pod to cluster IP hits both endpoints, unmasqueraded", 1306 sourceIP: "10.0.0.2", 1307 destIP: svcIP, 1308 destPort: svcPort, 1309 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1310 masq: false, 1311 }, 1312 { 1313 name: "pod to external IP hits both endpoints, unmasqueraded", 1314 sourceIP: "10.0.0.2", 1315 destIP: svcExternalIPs, 1316 destPort: svcPort, 1317 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1318 masq: false, 1319 }, 1320 { 1321 name: "external to external IP hits only local endpoint, unmasqueraded", 1322 sourceIP: testExternalClient, 1323 destIP: svcExternalIPs, 1324 destPort: svcPort, 1325 output: fmt.Sprintf("%s:%d", epIP2, svcPort), 1326 masq: false, 1327 }, 1328 { 1329 name: "pod to LB IP hits only both endpoints, unmasqueraded", 1330 sourceIP: "10.0.0.2", 1331 destIP: svcLBIP, 1332 destPort: svcPort, 1333 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1334 masq: false, 1335 }, 1336 { 1337 name: "external to LB IP hits only local endpoint, unmasqueraded", 1338 sourceIP: testExternalClient, 1339 destIP: svcLBIP, 1340 destPort: svcPort, 1341 output: fmt.Sprintf("%s:%d", epIP2, svcPort), 1342 masq: false, 1343 }, 1344 { 1345 name: "pod to NodePort hits both endpoints, unmasqueraded", 1346 sourceIP: "10.0.0.2", 1347 destIP: testNodeIP, 1348 destPort: svcNodePort, 1349 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1350 masq: false, 1351 }, 1352 { 1353 name: "external to NodePort hits only local endpoint, unmasqueraded", 1354 sourceIP: testExternalClient, 1355 destIP: testNodeIP, 1356 destPort: svcNodePort, 1357 output: fmt.Sprintf("%s:%d", epIP2, svcPort), 1358 masq: false, 1359 }, 1360 }) 1361 } 1362 1363 // TestExternalTrafficPolicyCluster tests that traffic to an externally-facing IP gets 1364 // masqueraded when using Cluster traffic policy. 1365 func TestExternalTrafficPolicyCluster(t *testing.T) { 1366 nft, fp := NewFakeProxier(v1.IPv4Protocol) 1367 1368 svcIP := "172.30.0.41" 1369 svcPort := 80 1370 svcNodePort := 3001 1371 svcExternalIPs := "192.168.99.11" 1372 svcLBIP := "1.2.3.4" 1373 svcPortName := proxy.ServicePortName{ 1374 NamespacedName: makeNSN("ns1", "svc1"), 1375 Port: "p80", 1376 } 1377 1378 makeServiceMap(fp, 1379 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 1380 svc.Spec.Type = v1.ServiceTypeLoadBalancer 1381 svc.Spec.ClusterIP = svcIP 1382 svc.Spec.ExternalIPs = []string{svcExternalIPs} 1383 svc.Spec.Ports = []v1.ServicePort{{ 1384 Name: svcPortName.Port, 1385 Port: int32(svcPort), 1386 Protocol: v1.ProtocolTCP, 1387 NodePort: int32(svcNodePort), 1388 TargetPort: intstr.FromInt32(int32(svcPort)), 1389 }} 1390 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 1391 IP: svcLBIP, 1392 }} 1393 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyCluster 1394 }), 1395 ) 1396 1397 epIP1 := "10.180.0.1" 1398 epIP2 := "10.180.2.1" 1399 populateEndpointSlices(fp, 1400 makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) { 1401 eps.AddressType = discovery.AddressTypeIPv4 1402 eps.Endpoints = []discovery.Endpoint{{ 1403 Addresses: []string{epIP1}, 1404 NodeName: nil, 1405 }, { 1406 Addresses: []string{epIP2}, 1407 NodeName: ptr.To(testHostname), 1408 }} 1409 eps.Ports = []discovery.EndpointPort{{ 1410 Name: ptr.To(svcPortName.Port), 1411 Port: ptr.To(int32(svcPort)), 1412 Protocol: ptr.To(v1.ProtocolTCP), 1413 }} 1414 }), 1415 ) 1416 1417 fp.syncProxyRules() 1418 1419 runPacketFlowTests(t, getLine(), nft, testNodeIPs, []packetFlowTest{ 1420 { 1421 name: "pod to cluster IP hits both endpoints, unmasqueraded", 1422 sourceIP: "10.0.0.2", 1423 destIP: svcIP, 1424 destPort: svcPort, 1425 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1426 masq: false, 1427 }, 1428 { 1429 name: "pod to external IP hits both endpoints, masqueraded", 1430 sourceIP: "10.0.0.2", 1431 destIP: svcExternalIPs, 1432 destPort: svcPort, 1433 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1434 masq: true, 1435 }, 1436 { 1437 name: "external to external IP hits both endpoints, masqueraded", 1438 sourceIP: testExternalClient, 1439 destIP: svcExternalIPs, 1440 destPort: svcPort, 1441 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1442 masq: true, 1443 }, 1444 { 1445 name: "pod to LB IP hits both endpoints, masqueraded", 1446 sourceIP: "10.0.0.2", 1447 destIP: svcLBIP, 1448 destPort: svcPort, 1449 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1450 masq: true, 1451 }, 1452 { 1453 name: "external to LB IP hits both endpoints, masqueraded", 1454 sourceIP: testExternalClient, 1455 destIP: svcLBIP, 1456 destPort: svcPort, 1457 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1458 masq: true, 1459 }, 1460 { 1461 name: "pod to NodePort hits both endpoints, masqueraded", 1462 sourceIP: "10.0.0.2", 1463 destIP: testNodeIP, 1464 destPort: svcNodePort, 1465 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1466 masq: true, 1467 }, 1468 { 1469 name: "external to NodePort hits both endpoints, masqueraded", 1470 sourceIP: testExternalClient, 1471 destIP: testNodeIP, 1472 destPort: svcNodePort, 1473 output: fmt.Sprintf("%s:%d, %s:%d", epIP1, svcPort, epIP2, svcPort), 1474 masq: true, 1475 }, 1476 }) 1477 } 1478 1479 func makeTestService(namespace, name string, svcFunc func(*v1.Service)) *v1.Service { 1480 svc := &v1.Service{ 1481 ObjectMeta: metav1.ObjectMeta{ 1482 Name: name, 1483 Namespace: namespace, 1484 Annotations: map[string]string{}, 1485 }, 1486 Spec: v1.ServiceSpec{}, 1487 Status: v1.ServiceStatus{}, 1488 } 1489 svcFunc(svc) 1490 return svc 1491 } 1492 1493 func addTestPort(array []v1.ServicePort, name string, protocol v1.Protocol, port, nodeport int32, targetPort int) []v1.ServicePort { 1494 svcPort := v1.ServicePort{ 1495 Name: name, 1496 Protocol: protocol, 1497 Port: port, 1498 NodePort: nodeport, 1499 TargetPort: intstr.FromInt32(int32(targetPort)), 1500 } 1501 return append(array, svcPort) 1502 } 1503 1504 func TestBuildServiceMapAddRemove(t *testing.T) { 1505 _, fp := NewFakeProxier(v1.IPv4Protocol) 1506 1507 services := []*v1.Service{ 1508 makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) { 1509 svc.Spec.Type = v1.ServiceTypeClusterIP 1510 svc.Spec.ClusterIP = "172.30.55.4" 1511 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0) 1512 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0) 1513 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpport", "SCTP", 1236, 6321, 0) 1514 }), 1515 makeTestService("somewhere-else", "node-port", func(svc *v1.Service) { 1516 svc.Spec.Type = v1.ServiceTypeNodePort 1517 svc.Spec.ClusterIP = "172.30.55.10" 1518 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blahblah", "UDP", 345, 678, 0) 1519 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "moreblahblah", "TCP", 344, 677, 0) 1520 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "muchmoreblah", "SCTP", 343, 676, 0) 1521 }), 1522 makeTestService("somewhere", "load-balancer", func(svc *v1.Service) { 1523 svc.Spec.Type = v1.ServiceTypeLoadBalancer 1524 svc.Spec.ClusterIP = "172.30.55.11" 1525 svc.Spec.LoadBalancerIP = "1.2.3.4" 1526 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar", "UDP", 8675, 30061, 7000) 1527 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8676, 30062, 7001) 1528 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 1529 Ingress: []v1.LoadBalancerIngress{ 1530 {IP: "1.2.3.4"}, 1531 }, 1532 } 1533 }), 1534 makeTestService("somewhere", "only-local-load-balancer", func(svc *v1.Service) { 1535 svc.Spec.Type = v1.ServiceTypeLoadBalancer 1536 svc.Spec.ClusterIP = "172.30.55.12" 1537 svc.Spec.LoadBalancerIP = "5.6.7.8" 1538 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar2", "UDP", 8677, 30063, 7002) 1539 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8678, 30064, 7003) 1540 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 1541 Ingress: []v1.LoadBalancerIngress{ 1542 {IP: "5.6.7.8"}, 1543 }, 1544 } 1545 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 1546 svc.Spec.HealthCheckNodePort = 345 1547 }), 1548 } 1549 1550 for i := range services { 1551 fp.OnServiceAdd(services[i]) 1552 } 1553 result := fp.svcPortMap.Update(fp.serviceChanges) 1554 if len(fp.svcPortMap) != 10 { 1555 t.Errorf("expected service map length 10, got %v", fp.svcPortMap) 1556 } 1557 1558 if len(result.DeletedUDPClusterIPs) != 0 { 1559 // Services only added, so nothing stale yet 1560 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 1561 } 1562 1563 // The only-local-loadbalancer ones get added 1564 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 1565 if len(healthCheckNodePorts) != 1 { 1566 t.Errorf("expected 1 healthcheck port, got %v", healthCheckNodePorts) 1567 } else { 1568 nsn := makeNSN("somewhere", "only-local-load-balancer") 1569 if port, found := healthCheckNodePorts[nsn]; !found || port != 345 { 1570 t.Errorf("expected healthcheck port [%q]=345: got %v", nsn, healthCheckNodePorts) 1571 } 1572 } 1573 1574 // Remove some stuff 1575 // oneService is a modification of services[0] with removed first port. 1576 oneService := makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) { 1577 svc.Spec.Type = v1.ServiceTypeClusterIP 1578 svc.Spec.ClusterIP = "172.30.55.4" 1579 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0) 1580 }) 1581 1582 fp.OnServiceUpdate(services[0], oneService) 1583 fp.OnServiceDelete(services[1]) 1584 fp.OnServiceDelete(services[2]) 1585 fp.OnServiceDelete(services[3]) 1586 1587 result = fp.svcPortMap.Update(fp.serviceChanges) 1588 if len(fp.svcPortMap) != 1 { 1589 t.Errorf("expected service map length 1, got %v", fp.svcPortMap) 1590 } 1591 1592 // All services but one were deleted. While you'd expect only the ClusterIPs 1593 // from the three deleted services here, we still have the ClusterIP for 1594 // the not-deleted service, because one of it's ServicePorts was deleted. 1595 expectedStaleUDPServices := []string{"172.30.55.10", "172.30.55.4", "172.30.55.11", "172.30.55.12"} 1596 if len(result.DeletedUDPClusterIPs) != len(expectedStaleUDPServices) { 1597 t.Errorf("expected stale UDP services length %d, got %v", len(expectedStaleUDPServices), result.DeletedUDPClusterIPs.UnsortedList()) 1598 } 1599 for _, ip := range expectedStaleUDPServices { 1600 if !result.DeletedUDPClusterIPs.Has(ip) { 1601 t.Errorf("expected stale UDP service service %s", ip) 1602 } 1603 } 1604 1605 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 1606 if len(healthCheckNodePorts) != 0 { 1607 t.Errorf("expected 0 healthcheck ports, got %v", healthCheckNodePorts) 1608 } 1609 } 1610 1611 func TestBuildServiceMapServiceHeadless(t *testing.T) { 1612 _, fp := NewFakeProxier(v1.IPv4Protocol) 1613 1614 makeServiceMap(fp, 1615 makeTestService("somewhere-else", "headless", func(svc *v1.Service) { 1616 svc.Spec.Type = v1.ServiceTypeClusterIP 1617 svc.Spec.ClusterIP = v1.ClusterIPNone 1618 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0) 1619 }), 1620 makeTestService("somewhere-else", "headless-without-port", func(svc *v1.Service) { 1621 svc.Spec.Type = v1.ServiceTypeClusterIP 1622 svc.Spec.ClusterIP = v1.ClusterIPNone 1623 }), 1624 ) 1625 1626 // Headless service should be ignored 1627 result := fp.svcPortMap.Update(fp.serviceChanges) 1628 if len(fp.svcPortMap) != 0 { 1629 t.Errorf("expected service map length 0, got %d", len(fp.svcPortMap)) 1630 } 1631 1632 if len(result.DeletedUDPClusterIPs) != 0 { 1633 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 1634 } 1635 1636 // No proxied services, so no healthchecks 1637 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 1638 if len(healthCheckNodePorts) != 0 { 1639 t.Errorf("expected healthcheck ports length 0, got %d", len(healthCheckNodePorts)) 1640 } 1641 } 1642 1643 func TestBuildServiceMapServiceTypeExternalName(t *testing.T) { 1644 _, fp := NewFakeProxier(v1.IPv4Protocol) 1645 1646 makeServiceMap(fp, 1647 makeTestService("somewhere-else", "external-name", func(svc *v1.Service) { 1648 svc.Spec.Type = v1.ServiceTypeExternalName 1649 svc.Spec.ClusterIP = "172.30.55.4" // Should be ignored 1650 svc.Spec.ExternalName = "foo2.bar.com" 1651 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blah", "UDP", 1235, 5321, 0) 1652 }), 1653 ) 1654 1655 result := fp.svcPortMap.Update(fp.serviceChanges) 1656 if len(fp.svcPortMap) != 0 { 1657 t.Errorf("expected service map length 0, got %v", fp.svcPortMap) 1658 } 1659 if len(result.DeletedUDPClusterIPs) != 0 { 1660 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs) 1661 } 1662 // No proxied services, so no healthchecks 1663 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 1664 if len(healthCheckNodePorts) != 0 { 1665 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 1666 } 1667 } 1668 1669 func TestBuildServiceMapServiceUpdate(t *testing.T) { 1670 _, fp := NewFakeProxier(v1.IPv4Protocol) 1671 1672 servicev1 := makeTestService("somewhere", "some-service", func(svc *v1.Service) { 1673 svc.Spec.Type = v1.ServiceTypeClusterIP 1674 svc.Spec.ClusterIP = "172.30.55.4" 1675 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0) 1676 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 0) 1677 }) 1678 servicev2 := makeTestService("somewhere", "some-service", func(svc *v1.Service) { 1679 svc.Spec.Type = v1.ServiceTypeLoadBalancer 1680 svc.Spec.ClusterIP = "172.30.55.4" 1681 svc.Spec.LoadBalancerIP = "1.2.3.4" 1682 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 7002) 1683 svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 7003) 1684 svc.Status.LoadBalancer = v1.LoadBalancerStatus{ 1685 Ingress: []v1.LoadBalancerIngress{ 1686 {IP: "1.2.3.4"}, 1687 }, 1688 } 1689 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 1690 svc.Spec.HealthCheckNodePort = 345 1691 }) 1692 1693 fp.OnServiceAdd(servicev1) 1694 1695 result := fp.svcPortMap.Update(fp.serviceChanges) 1696 if len(fp.svcPortMap) != 2 { 1697 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 1698 } 1699 if len(result.DeletedUDPClusterIPs) != 0 { 1700 // Services only added, so nothing stale yet 1701 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 1702 } 1703 healthCheckNodePorts := fp.svcPortMap.HealthCheckNodePorts() 1704 if len(healthCheckNodePorts) != 0 { 1705 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 1706 } 1707 1708 // Change service to load-balancer 1709 fp.OnServiceUpdate(servicev1, servicev2) 1710 result = fp.svcPortMap.Update(fp.serviceChanges) 1711 if len(fp.svcPortMap) != 2 { 1712 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 1713 } 1714 if len(result.DeletedUDPClusterIPs) != 0 { 1715 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList()) 1716 } 1717 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 1718 if len(healthCheckNodePorts) != 1 { 1719 t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts) 1720 } 1721 1722 // No change; make sure the service map stays the same and there are 1723 // no health-check changes 1724 fp.OnServiceUpdate(servicev2, servicev2) 1725 result = fp.svcPortMap.Update(fp.serviceChanges) 1726 if len(fp.svcPortMap) != 2 { 1727 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 1728 } 1729 if len(result.DeletedUDPClusterIPs) != 0 { 1730 t.Errorf("expected stale UDP services length 0, got %v", result.DeletedUDPClusterIPs.UnsortedList()) 1731 } 1732 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 1733 if len(healthCheckNodePorts) != 1 { 1734 t.Errorf("expected healthcheck ports length 1, got %v", healthCheckNodePorts) 1735 } 1736 1737 // And back to ClusterIP 1738 fp.OnServiceUpdate(servicev2, servicev1) 1739 result = fp.svcPortMap.Update(fp.serviceChanges) 1740 if len(fp.svcPortMap) != 2 { 1741 t.Errorf("expected service map length 2, got %v", fp.svcPortMap) 1742 } 1743 if len(result.DeletedUDPClusterIPs) != 0 { 1744 // Services only added, so nothing stale yet 1745 t.Errorf("expected stale UDP services length 0, got %d", len(result.DeletedUDPClusterIPs)) 1746 } 1747 healthCheckNodePorts = fp.svcPortMap.HealthCheckNodePorts() 1748 if len(healthCheckNodePorts) != 0 { 1749 t.Errorf("expected healthcheck ports length 0, got %v", healthCheckNodePorts) 1750 } 1751 } 1752 1753 func populateEndpointSlices(proxier *Proxier, allEndpointSlices ...*discovery.EndpointSlice) { 1754 for i := range allEndpointSlices { 1755 proxier.OnEndpointSliceAdd(allEndpointSlices[i]) 1756 } 1757 } 1758 1759 func makeTestEndpointSlice(namespace, name string, sliceNum int, epsFunc func(*discovery.EndpointSlice)) *discovery.EndpointSlice { 1760 eps := &discovery.EndpointSlice{ 1761 ObjectMeta: metav1.ObjectMeta{ 1762 Name: fmt.Sprintf("%s-%d", name, sliceNum), 1763 Namespace: namespace, 1764 Labels: map[string]string{discovery.LabelServiceName: name}, 1765 }, 1766 } 1767 epsFunc(eps) 1768 return eps 1769 } 1770 1771 func makeNSN(namespace, name string) types.NamespacedName { 1772 return types.NamespacedName{Namespace: namespace, Name: name} 1773 } 1774 1775 func makeServicePortName(ns, name, port string, protocol v1.Protocol) proxy.ServicePortName { 1776 return proxy.ServicePortName{ 1777 NamespacedName: makeNSN(ns, name), 1778 Port: port, 1779 Protocol: protocol, 1780 } 1781 } 1782 1783 func makeServiceMap(proxier *Proxier, allServices ...*v1.Service) { 1784 for i := range allServices { 1785 proxier.OnServiceAdd(allServices[i]) 1786 } 1787 1788 proxier.mu.Lock() 1789 defer proxier.mu.Unlock() 1790 proxier.servicesSynced = true 1791 } 1792 1793 type endpointExpectation struct { 1794 endpoint string 1795 isLocal bool 1796 } 1797 1798 func checkEndpointExpectations(t *testing.T, tci int, newMap proxy.EndpointsMap, expected map[proxy.ServicePortName][]endpointExpectation) { 1799 if len(newMap) != len(expected) { 1800 t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap) 1801 } 1802 for x := range expected { 1803 if len(newMap[x]) != len(expected[x]) { 1804 t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x])) 1805 } else { 1806 for i := range expected[x] { 1807 newEp := newMap[x][i] 1808 if newEp.String() != expected[x][i].endpoint || 1809 newEp.IsLocal() != expected[x][i].isLocal { 1810 t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, expected[x][i], newEp) 1811 } 1812 } 1813 } 1814 } 1815 } 1816 1817 func TestUpdateEndpointsMap(t *testing.T) { 1818 emptyEndpointSlices := []*discovery.EndpointSlice{ 1819 makeTestEndpointSlice("ns1", "ep1", 1, func(*discovery.EndpointSlice) {}), 1820 } 1821 subset1 := func(eps *discovery.EndpointSlice) { 1822 eps.AddressType = discovery.AddressTypeIPv4 1823 eps.Endpoints = []discovery.Endpoint{{ 1824 Addresses: []string{"10.1.1.1"}, 1825 }} 1826 eps.Ports = []discovery.EndpointPort{{ 1827 Name: ptr.To("p11"), 1828 Port: ptr.To[int32](11), 1829 Protocol: ptr.To(v1.ProtocolUDP), 1830 }} 1831 } 1832 subset2 := func(eps *discovery.EndpointSlice) { 1833 eps.AddressType = discovery.AddressTypeIPv4 1834 eps.Endpoints = []discovery.Endpoint{{ 1835 Addresses: []string{"10.1.1.2"}, 1836 }} 1837 eps.Ports = []discovery.EndpointPort{{ 1838 Name: ptr.To("p12"), 1839 Port: ptr.To[int32](12), 1840 Protocol: ptr.To(v1.ProtocolUDP), 1841 }} 1842 } 1843 namedPortLocal := []*discovery.EndpointSlice{ 1844 makeTestEndpointSlice("ns1", "ep1", 1, 1845 func(eps *discovery.EndpointSlice) { 1846 eps.AddressType = discovery.AddressTypeIPv4 1847 eps.Endpoints = []discovery.Endpoint{{ 1848 Addresses: []string{"10.1.1.1"}, 1849 NodeName: ptr.To(testHostname), 1850 }} 1851 eps.Ports = []discovery.EndpointPort{{ 1852 Name: ptr.To("p11"), 1853 Port: ptr.To[int32](11), 1854 Protocol: ptr.To(v1.ProtocolUDP), 1855 }} 1856 }), 1857 } 1858 namedPort := []*discovery.EndpointSlice{ 1859 makeTestEndpointSlice("ns1", "ep1", 1, subset1), 1860 } 1861 namedPortRenamed := []*discovery.EndpointSlice{ 1862 makeTestEndpointSlice("ns1", "ep1", 1, 1863 func(eps *discovery.EndpointSlice) { 1864 eps.AddressType = discovery.AddressTypeIPv4 1865 eps.Endpoints = []discovery.Endpoint{{ 1866 Addresses: []string{"10.1.1.1"}, 1867 }} 1868 eps.Ports = []discovery.EndpointPort{{ 1869 Name: ptr.To("p11-2"), 1870 Port: ptr.To[int32](11), 1871 Protocol: ptr.To(v1.ProtocolUDP), 1872 }} 1873 }), 1874 } 1875 namedPortRenumbered := []*discovery.EndpointSlice{ 1876 makeTestEndpointSlice("ns1", "ep1", 1, 1877 func(eps *discovery.EndpointSlice) { 1878 eps.AddressType = discovery.AddressTypeIPv4 1879 eps.Endpoints = []discovery.Endpoint{{ 1880 Addresses: []string{"10.1.1.1"}, 1881 }} 1882 eps.Ports = []discovery.EndpointPort{{ 1883 Name: ptr.To("p11"), 1884 Port: ptr.To[int32](22), 1885 Protocol: ptr.To(v1.ProtocolUDP), 1886 }} 1887 }), 1888 } 1889 namedPortsLocalNoLocal := []*discovery.EndpointSlice{ 1890 makeTestEndpointSlice("ns1", "ep1", 1, 1891 func(eps *discovery.EndpointSlice) { 1892 eps.AddressType = discovery.AddressTypeIPv4 1893 eps.Endpoints = []discovery.Endpoint{{ 1894 Addresses: []string{"10.1.1.1"}, 1895 }, { 1896 Addresses: []string{"10.1.1.2"}, 1897 NodeName: ptr.To(testHostname), 1898 }} 1899 eps.Ports = []discovery.EndpointPort{{ 1900 Name: ptr.To("p11"), 1901 Port: ptr.To[int32](11), 1902 Protocol: ptr.To(v1.ProtocolUDP), 1903 }, { 1904 Name: ptr.To("p12"), 1905 Port: ptr.To[int32](12), 1906 Protocol: ptr.To(v1.ProtocolUDP), 1907 }} 1908 }), 1909 } 1910 multipleSubsets := []*discovery.EndpointSlice{ 1911 makeTestEndpointSlice("ns1", "ep1", 1, subset1), 1912 makeTestEndpointSlice("ns1", "ep1", 2, subset2), 1913 } 1914 subsetLocal := func(eps *discovery.EndpointSlice) { 1915 eps.AddressType = discovery.AddressTypeIPv4 1916 eps.Endpoints = []discovery.Endpoint{{ 1917 Addresses: []string{"10.1.1.2"}, 1918 NodeName: ptr.To(testHostname), 1919 }} 1920 eps.Ports = []discovery.EndpointPort{{ 1921 Name: ptr.To("p12"), 1922 Port: ptr.To[int32](12), 1923 Protocol: ptr.To(v1.ProtocolUDP), 1924 }} 1925 } 1926 multipleSubsetsWithLocal := []*discovery.EndpointSlice{ 1927 makeTestEndpointSlice("ns1", "ep1", 1, subset1), 1928 makeTestEndpointSlice("ns1", "ep1", 2, subsetLocal), 1929 } 1930 subsetMultiplePortsLocal := func(eps *discovery.EndpointSlice) { 1931 eps.AddressType = discovery.AddressTypeIPv4 1932 eps.Endpoints = []discovery.Endpoint{{ 1933 Addresses: []string{"10.1.1.1"}, 1934 NodeName: ptr.To(testHostname), 1935 }} 1936 eps.Ports = []discovery.EndpointPort{{ 1937 Name: ptr.To("p11"), 1938 Port: ptr.To[int32](11), 1939 Protocol: ptr.To(v1.ProtocolUDP), 1940 }, { 1941 Name: ptr.To("p12"), 1942 Port: ptr.To[int32](12), 1943 Protocol: ptr.To(v1.ProtocolUDP), 1944 }} 1945 } 1946 subset3 := func(eps *discovery.EndpointSlice) { 1947 eps.AddressType = discovery.AddressTypeIPv4 1948 eps.Endpoints = []discovery.Endpoint{{ 1949 Addresses: []string{"10.1.1.3"}, 1950 }} 1951 eps.Ports = []discovery.EndpointPort{{ 1952 Name: ptr.To("p13"), 1953 Port: ptr.To[int32](13), 1954 Protocol: ptr.To(v1.ProtocolUDP), 1955 }} 1956 } 1957 multipleSubsetsMultiplePortsLocal := []*discovery.EndpointSlice{ 1958 makeTestEndpointSlice("ns1", "ep1", 1, subsetMultiplePortsLocal), 1959 makeTestEndpointSlice("ns1", "ep1", 2, subset3), 1960 } 1961 subsetMultipleIPsPorts1 := func(eps *discovery.EndpointSlice) { 1962 eps.AddressType = discovery.AddressTypeIPv4 1963 eps.Endpoints = []discovery.Endpoint{{ 1964 Addresses: []string{"10.1.1.1"}, 1965 }, { 1966 Addresses: []string{"10.1.1.2"}, 1967 NodeName: ptr.To(testHostname), 1968 }} 1969 eps.Ports = []discovery.EndpointPort{{ 1970 Name: ptr.To("p11"), 1971 Port: ptr.To[int32](11), 1972 Protocol: ptr.To(v1.ProtocolUDP), 1973 }, { 1974 Name: ptr.To("p12"), 1975 Port: ptr.To[int32](12), 1976 Protocol: ptr.To(v1.ProtocolUDP), 1977 }} 1978 } 1979 subsetMultipleIPsPorts2 := func(eps *discovery.EndpointSlice) { 1980 eps.AddressType = discovery.AddressTypeIPv4 1981 eps.Endpoints = []discovery.Endpoint{{ 1982 Addresses: []string{"10.1.1.3"}, 1983 }, { 1984 Addresses: []string{"10.1.1.4"}, 1985 NodeName: ptr.To(testHostname), 1986 }} 1987 eps.Ports = []discovery.EndpointPort{{ 1988 Name: ptr.To("p13"), 1989 Port: ptr.To[int32](13), 1990 Protocol: ptr.To(v1.ProtocolUDP), 1991 }, { 1992 Name: ptr.To("p14"), 1993 Port: ptr.To[int32](14), 1994 Protocol: ptr.To(v1.ProtocolUDP), 1995 }} 1996 } 1997 subsetMultipleIPsPorts3 := func(eps *discovery.EndpointSlice) { 1998 eps.AddressType = discovery.AddressTypeIPv4 1999 eps.Endpoints = []discovery.Endpoint{{ 2000 Addresses: []string{"10.2.2.1"}, 2001 }, { 2002 Addresses: []string{"10.2.2.2"}, 2003 NodeName: ptr.To(testHostname), 2004 }} 2005 eps.Ports = []discovery.EndpointPort{{ 2006 Name: ptr.To("p21"), 2007 Port: ptr.To[int32](21), 2008 Protocol: ptr.To(v1.ProtocolUDP), 2009 }, { 2010 Name: ptr.To("p22"), 2011 Port: ptr.To[int32](22), 2012 Protocol: ptr.To(v1.ProtocolUDP), 2013 }} 2014 } 2015 multipleSubsetsIPsPorts := []*discovery.EndpointSlice{ 2016 makeTestEndpointSlice("ns1", "ep1", 1, subsetMultipleIPsPorts1), 2017 makeTestEndpointSlice("ns1", "ep1", 2, subsetMultipleIPsPorts2), 2018 makeTestEndpointSlice("ns2", "ep2", 1, subsetMultipleIPsPorts3), 2019 } 2020 complexSubset1 := func(eps *discovery.EndpointSlice) { 2021 eps.AddressType = discovery.AddressTypeIPv4 2022 eps.Endpoints = []discovery.Endpoint{{ 2023 Addresses: []string{"10.2.2.2"}, 2024 NodeName: ptr.To(testHostname), 2025 }, { 2026 Addresses: []string{"10.2.2.22"}, 2027 NodeName: ptr.To(testHostname), 2028 }} 2029 eps.Ports = []discovery.EndpointPort{{ 2030 Name: ptr.To("p22"), 2031 Port: ptr.To[int32](22), 2032 Protocol: ptr.To(v1.ProtocolUDP), 2033 }} 2034 } 2035 complexSubset2 := func(eps *discovery.EndpointSlice) { 2036 eps.AddressType = discovery.AddressTypeIPv4 2037 eps.Endpoints = []discovery.Endpoint{{ 2038 Addresses: []string{"10.2.2.3"}, 2039 NodeName: ptr.To(testHostname), 2040 }} 2041 eps.Ports = []discovery.EndpointPort{{ 2042 Name: ptr.To("p23"), 2043 Port: ptr.To[int32](23), 2044 Protocol: ptr.To(v1.ProtocolUDP), 2045 }} 2046 } 2047 complexSubset3 := func(eps *discovery.EndpointSlice) { 2048 eps.AddressType = discovery.AddressTypeIPv4 2049 eps.Endpoints = []discovery.Endpoint{{ 2050 Addresses: []string{"10.4.4.4"}, 2051 NodeName: ptr.To(testHostname), 2052 }, { 2053 Addresses: []string{"10.4.4.5"}, 2054 NodeName: ptr.To(testHostname), 2055 }} 2056 eps.Ports = []discovery.EndpointPort{{ 2057 Name: ptr.To("p44"), 2058 Port: ptr.To[int32](44), 2059 Protocol: ptr.To(v1.ProtocolUDP), 2060 }} 2061 } 2062 complexSubset4 := func(eps *discovery.EndpointSlice) { 2063 eps.AddressType = discovery.AddressTypeIPv4 2064 eps.Endpoints = []discovery.Endpoint{{ 2065 Addresses: []string{"10.4.4.6"}, 2066 NodeName: ptr.To(testHostname), 2067 }} 2068 eps.Ports = []discovery.EndpointPort{{ 2069 Name: ptr.To("p45"), 2070 Port: ptr.To[int32](45), 2071 Protocol: ptr.To(v1.ProtocolUDP), 2072 }} 2073 } 2074 complexSubset5 := func(eps *discovery.EndpointSlice) { 2075 eps.AddressType = discovery.AddressTypeIPv4 2076 eps.Endpoints = []discovery.Endpoint{{ 2077 Addresses: []string{"10.1.1.1"}, 2078 }, { 2079 Addresses: []string{"10.1.1.11"}, 2080 }} 2081 eps.Ports = []discovery.EndpointPort{{ 2082 Name: ptr.To("p11"), 2083 Port: ptr.To[int32](11), 2084 Protocol: ptr.To(v1.ProtocolUDP), 2085 }} 2086 } 2087 complexSubset6 := func(eps *discovery.EndpointSlice) { 2088 eps.AddressType = discovery.AddressTypeIPv4 2089 eps.Endpoints = []discovery.Endpoint{{ 2090 Addresses: []string{"10.1.1.2"}, 2091 }} 2092 eps.Ports = []discovery.EndpointPort{{ 2093 Name: ptr.To("p12"), 2094 Port: ptr.To[int32](12), 2095 Protocol: ptr.To(v1.ProtocolUDP), 2096 }, { 2097 Name: ptr.To("p122"), 2098 Port: ptr.To[int32](122), 2099 Protocol: ptr.To(v1.ProtocolUDP), 2100 }} 2101 } 2102 complexSubset7 := func(eps *discovery.EndpointSlice) { 2103 eps.AddressType = discovery.AddressTypeIPv4 2104 eps.Endpoints = []discovery.Endpoint{{ 2105 Addresses: []string{"10.3.3.3"}, 2106 }} 2107 eps.Ports = []discovery.EndpointPort{{ 2108 Name: ptr.To("p33"), 2109 Port: ptr.To[int32](33), 2110 Protocol: ptr.To(v1.ProtocolUDP), 2111 }} 2112 } 2113 complexSubset8 := func(eps *discovery.EndpointSlice) { 2114 eps.AddressType = discovery.AddressTypeIPv4 2115 eps.Endpoints = []discovery.Endpoint{{ 2116 Addresses: []string{"10.4.4.4"}, 2117 NodeName: ptr.To(testHostname), 2118 }} 2119 eps.Ports = []discovery.EndpointPort{{ 2120 Name: ptr.To("p44"), 2121 Port: ptr.To[int32](44), 2122 Protocol: ptr.To(v1.ProtocolUDP), 2123 }} 2124 } 2125 complexBefore := []*discovery.EndpointSlice{ 2126 makeTestEndpointSlice("ns1", "ep1", 1, subset1), 2127 nil, 2128 makeTestEndpointSlice("ns2", "ep2", 1, complexSubset1), 2129 makeTestEndpointSlice("ns2", "ep2", 2, complexSubset2), 2130 nil, 2131 makeTestEndpointSlice("ns4", "ep4", 1, complexSubset3), 2132 makeTestEndpointSlice("ns4", "ep4", 2, complexSubset4), 2133 } 2134 complexAfter := []*discovery.EndpointSlice{ 2135 makeTestEndpointSlice("ns1", "ep1", 1, complexSubset5), 2136 makeTestEndpointSlice("ns1", "ep1", 2, complexSubset6), 2137 nil, 2138 nil, 2139 makeTestEndpointSlice("ns3", "ep3", 1, complexSubset7), 2140 makeTestEndpointSlice("ns4", "ep4", 1, complexSubset8), 2141 nil, 2142 } 2143 2144 testCases := []struct { 2145 // previousEndpoints and currentEndpoints are used to call appropriate 2146 // handlers OnEndpoints* (based on whether corresponding values are nil 2147 // or non-nil) and must be of equal length. 2148 name string 2149 previousEndpoints []*discovery.EndpointSlice 2150 currentEndpoints []*discovery.EndpointSlice 2151 oldEndpoints map[proxy.ServicePortName][]endpointExpectation 2152 expectedResult map[proxy.ServicePortName][]endpointExpectation 2153 expectedDeletedUDPEndpoints []proxy.ServiceEndpoint 2154 expectedNewlyActiveUDPServices map[proxy.ServicePortName]bool 2155 expectedLocalEndpoints map[types.NamespacedName]int 2156 }{{ 2157 // Case[0]: nothing 2158 name: "nothing", 2159 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{}, 2160 expectedResult: map[proxy.ServicePortName][]endpointExpectation{}, 2161 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2162 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2163 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2164 }, { 2165 // Case[1]: no change, named port, local 2166 name: "no change, named port, local", 2167 previousEndpoints: namedPortLocal, 2168 currentEndpoints: namedPortLocal, 2169 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2170 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2171 {endpoint: "10.1.1.1:11", isLocal: true}, 2172 }, 2173 }, 2174 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2175 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2176 {endpoint: "10.1.1.1:11", isLocal: true}, 2177 }, 2178 }, 2179 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2180 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2181 expectedLocalEndpoints: map[types.NamespacedName]int{ 2182 makeNSN("ns1", "ep1"): 1, 2183 }, 2184 }, { 2185 // Case[2]: no change, multiple subsets 2186 name: "no change, multiple subsets", 2187 previousEndpoints: multipleSubsets, 2188 currentEndpoints: multipleSubsets, 2189 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2190 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2191 {endpoint: "10.1.1.1:11", isLocal: false}, 2192 }, 2193 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2194 {endpoint: "10.1.1.2:12", isLocal: false}, 2195 }, 2196 }, 2197 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2198 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2199 {endpoint: "10.1.1.1:11", isLocal: false}, 2200 }, 2201 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2202 {endpoint: "10.1.1.2:12", isLocal: false}, 2203 }, 2204 }, 2205 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2206 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2207 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2208 }, { 2209 // Case[3]: no change, multiple subsets, multiple ports, local 2210 name: "no change, multiple subsets, multiple ports, local", 2211 previousEndpoints: multipleSubsetsMultiplePortsLocal, 2212 currentEndpoints: multipleSubsetsMultiplePortsLocal, 2213 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2214 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2215 {endpoint: "10.1.1.1:11", isLocal: true}, 2216 }, 2217 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2218 {endpoint: "10.1.1.1:12", isLocal: true}, 2219 }, 2220 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): { 2221 {endpoint: "10.1.1.3:13", isLocal: false}, 2222 }, 2223 }, 2224 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2225 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2226 {endpoint: "10.1.1.1:11", isLocal: true}, 2227 }, 2228 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2229 {endpoint: "10.1.1.1:12", isLocal: true}, 2230 }, 2231 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): { 2232 {endpoint: "10.1.1.3:13", isLocal: false}, 2233 }, 2234 }, 2235 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2236 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2237 expectedLocalEndpoints: map[types.NamespacedName]int{ 2238 makeNSN("ns1", "ep1"): 1, 2239 }, 2240 }, { 2241 // Case[4]: no change, multiple endpoints, subsets, IPs, and ports 2242 name: "no change, multiple endpoints, subsets, IPs, and ports", 2243 previousEndpoints: multipleSubsetsIPsPorts, 2244 currentEndpoints: multipleSubsetsIPsPorts, 2245 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2246 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2247 {endpoint: "10.1.1.1:11", isLocal: false}, 2248 {endpoint: "10.1.1.2:11", isLocal: true}, 2249 }, 2250 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2251 {endpoint: "10.1.1.1:12", isLocal: false}, 2252 {endpoint: "10.1.1.2:12", isLocal: true}, 2253 }, 2254 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): { 2255 {endpoint: "10.1.1.3:13", isLocal: false}, 2256 {endpoint: "10.1.1.4:13", isLocal: true}, 2257 }, 2258 makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): { 2259 {endpoint: "10.1.1.3:14", isLocal: false}, 2260 {endpoint: "10.1.1.4:14", isLocal: true}, 2261 }, 2262 makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): { 2263 {endpoint: "10.2.2.1:21", isLocal: false}, 2264 {endpoint: "10.2.2.2:21", isLocal: true}, 2265 }, 2266 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): { 2267 {endpoint: "10.2.2.1:22", isLocal: false}, 2268 {endpoint: "10.2.2.2:22", isLocal: true}, 2269 }, 2270 }, 2271 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2272 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2273 {endpoint: "10.1.1.1:11", isLocal: false}, 2274 {endpoint: "10.1.1.2:11", isLocal: true}, 2275 }, 2276 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2277 {endpoint: "10.1.1.1:12", isLocal: false}, 2278 {endpoint: "10.1.1.2:12", isLocal: true}, 2279 }, 2280 makeServicePortName("ns1", "ep1", "p13", v1.ProtocolUDP): { 2281 {endpoint: "10.1.1.3:13", isLocal: false}, 2282 {endpoint: "10.1.1.4:13", isLocal: true}, 2283 }, 2284 makeServicePortName("ns1", "ep1", "p14", v1.ProtocolUDP): { 2285 {endpoint: "10.1.1.3:14", isLocal: false}, 2286 {endpoint: "10.1.1.4:14", isLocal: true}, 2287 }, 2288 makeServicePortName("ns2", "ep2", "p21", v1.ProtocolUDP): { 2289 {endpoint: "10.2.2.1:21", isLocal: false}, 2290 {endpoint: "10.2.2.2:21", isLocal: true}, 2291 }, 2292 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): { 2293 {endpoint: "10.2.2.1:22", isLocal: false}, 2294 {endpoint: "10.2.2.2:22", isLocal: true}, 2295 }, 2296 }, 2297 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2298 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2299 expectedLocalEndpoints: map[types.NamespacedName]int{ 2300 makeNSN("ns1", "ep1"): 2, 2301 makeNSN("ns2", "ep2"): 1, 2302 }, 2303 }, { 2304 // Case[5]: add an Endpoints 2305 name: "add an Endpoints", 2306 previousEndpoints: []*discovery.EndpointSlice{nil}, 2307 currentEndpoints: namedPortLocal, 2308 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{}, 2309 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2310 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2311 {endpoint: "10.1.1.1:11", isLocal: true}, 2312 }, 2313 }, 2314 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2315 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2316 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): true, 2317 }, 2318 expectedLocalEndpoints: map[types.NamespacedName]int{ 2319 makeNSN("ns1", "ep1"): 1, 2320 }, 2321 }, { 2322 // Case[6]: remove an Endpoints 2323 name: "remove an Endpoints", 2324 previousEndpoints: namedPortLocal, 2325 currentEndpoints: []*discovery.EndpointSlice{nil}, 2326 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2327 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2328 {endpoint: "10.1.1.1:11", isLocal: true}, 2329 }, 2330 }, 2331 expectedResult: map[proxy.ServicePortName][]endpointExpectation{}, 2332 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2333 Endpoint: "10.1.1.1:11", 2334 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP), 2335 }}, 2336 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2337 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2338 }, { 2339 // Case[7]: add an IP and port 2340 name: "add an IP and port", 2341 previousEndpoints: namedPort, 2342 currentEndpoints: namedPortsLocalNoLocal, 2343 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2344 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2345 {endpoint: "10.1.1.1:11", isLocal: false}, 2346 }, 2347 }, 2348 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2349 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2350 {endpoint: "10.1.1.1:11", isLocal: false}, 2351 {endpoint: "10.1.1.2:11", isLocal: true}, 2352 }, 2353 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2354 {endpoint: "10.1.1.1:12", isLocal: false}, 2355 {endpoint: "10.1.1.2:12", isLocal: true}, 2356 }, 2357 }, 2358 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2359 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2360 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true, 2361 }, 2362 expectedLocalEndpoints: map[types.NamespacedName]int{ 2363 makeNSN("ns1", "ep1"): 1, 2364 }, 2365 }, { 2366 // Case[8]: remove an IP and port 2367 name: "remove an IP and port", 2368 previousEndpoints: namedPortsLocalNoLocal, 2369 currentEndpoints: namedPort, 2370 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2371 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2372 {endpoint: "10.1.1.1:11", isLocal: false}, 2373 {endpoint: "10.1.1.2:11", isLocal: true}, 2374 }, 2375 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2376 {endpoint: "10.1.1.1:12", isLocal: false}, 2377 {endpoint: "10.1.1.2:12", isLocal: true}, 2378 }, 2379 }, 2380 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2381 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2382 {endpoint: "10.1.1.1:11", isLocal: false}, 2383 }, 2384 }, 2385 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2386 Endpoint: "10.1.1.2:11", 2387 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP), 2388 }, { 2389 Endpoint: "10.1.1.1:12", 2390 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP), 2391 }, { 2392 Endpoint: "10.1.1.2:12", 2393 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP), 2394 }}, 2395 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2396 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2397 }, { 2398 // Case[9]: add a subset 2399 name: "add a subset", 2400 previousEndpoints: []*discovery.EndpointSlice{namedPort[0], nil}, 2401 currentEndpoints: multipleSubsetsWithLocal, 2402 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2403 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2404 {endpoint: "10.1.1.1:11", isLocal: false}, 2405 }, 2406 }, 2407 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2408 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2409 {endpoint: "10.1.1.1:11", isLocal: false}, 2410 }, 2411 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2412 {endpoint: "10.1.1.2:12", isLocal: true}, 2413 }, 2414 }, 2415 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2416 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2417 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true, 2418 }, 2419 expectedLocalEndpoints: map[types.NamespacedName]int{ 2420 makeNSN("ns1", "ep1"): 1, 2421 }, 2422 }, { 2423 // Case[10]: remove a subset 2424 name: "remove a subset", 2425 previousEndpoints: multipleSubsets, 2426 currentEndpoints: []*discovery.EndpointSlice{namedPort[0], nil}, 2427 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2428 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2429 {endpoint: "10.1.1.1:11", isLocal: false}, 2430 }, 2431 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2432 {endpoint: "10.1.1.2:12", isLocal: false}, 2433 }, 2434 }, 2435 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2436 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2437 {endpoint: "10.1.1.1:11", isLocal: false}, 2438 }, 2439 }, 2440 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2441 Endpoint: "10.1.1.2:12", 2442 ServicePortName: makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP), 2443 }}, 2444 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2445 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2446 }, { 2447 // Case[11]: rename a port 2448 name: "rename a port", 2449 previousEndpoints: namedPort, 2450 currentEndpoints: namedPortRenamed, 2451 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2452 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2453 {endpoint: "10.1.1.1:11", isLocal: false}, 2454 }, 2455 }, 2456 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2457 makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): { 2458 {endpoint: "10.1.1.1:11", isLocal: false}, 2459 }, 2460 }, 2461 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2462 Endpoint: "10.1.1.1:11", 2463 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP), 2464 }}, 2465 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2466 makeServicePortName("ns1", "ep1", "p11-2", v1.ProtocolUDP): true, 2467 }, 2468 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2469 }, { 2470 // Case[12]: renumber a port 2471 name: "renumber a port", 2472 previousEndpoints: namedPort, 2473 currentEndpoints: namedPortRenumbered, 2474 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2475 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2476 {endpoint: "10.1.1.1:11", isLocal: false}, 2477 }, 2478 }, 2479 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2480 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2481 {endpoint: "10.1.1.1:22", isLocal: false}, 2482 }, 2483 }, 2484 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2485 Endpoint: "10.1.1.1:11", 2486 ServicePortName: makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP), 2487 }}, 2488 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{}, 2489 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2490 }, { 2491 // Case[13]: complex add and remove 2492 name: "complex add and remove", 2493 previousEndpoints: complexBefore, 2494 currentEndpoints: complexAfter, 2495 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{ 2496 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2497 {endpoint: "10.1.1.1:11", isLocal: false}, 2498 }, 2499 makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP): { 2500 {endpoint: "10.2.2.22:22", isLocal: true}, 2501 {endpoint: "10.2.2.2:22", isLocal: true}, 2502 }, 2503 makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP): { 2504 {endpoint: "10.2.2.3:23", isLocal: true}, 2505 }, 2506 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): { 2507 {endpoint: "10.4.4.4:44", isLocal: true}, 2508 {endpoint: "10.4.4.5:44", isLocal: true}, 2509 }, 2510 makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP): { 2511 {endpoint: "10.4.4.6:45", isLocal: true}, 2512 }, 2513 }, 2514 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2515 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2516 {endpoint: "10.1.1.11:11", isLocal: false}, 2517 {endpoint: "10.1.1.1:11", isLocal: false}, 2518 }, 2519 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): { 2520 {endpoint: "10.1.1.2:12", isLocal: false}, 2521 }, 2522 makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): { 2523 {endpoint: "10.1.1.2:122", isLocal: false}, 2524 }, 2525 makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP): { 2526 {endpoint: "10.3.3.3:33", isLocal: false}, 2527 }, 2528 makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP): { 2529 {endpoint: "10.4.4.4:44", isLocal: true}, 2530 }, 2531 }, 2532 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 2533 Endpoint: "10.2.2.2:22", 2534 ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP), 2535 }, { 2536 Endpoint: "10.2.2.22:22", 2537 ServicePortName: makeServicePortName("ns2", "ep2", "p22", v1.ProtocolUDP), 2538 }, { 2539 Endpoint: "10.2.2.3:23", 2540 ServicePortName: makeServicePortName("ns2", "ep2", "p23", v1.ProtocolUDP), 2541 }, { 2542 Endpoint: "10.4.4.5:44", 2543 ServicePortName: makeServicePortName("ns4", "ep4", "p44", v1.ProtocolUDP), 2544 }, { 2545 Endpoint: "10.4.4.6:45", 2546 ServicePortName: makeServicePortName("ns4", "ep4", "p45", v1.ProtocolUDP), 2547 }}, 2548 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2549 makeServicePortName("ns1", "ep1", "p12", v1.ProtocolUDP): true, 2550 makeServicePortName("ns1", "ep1", "p122", v1.ProtocolUDP): true, 2551 makeServicePortName("ns3", "ep3", "p33", v1.ProtocolUDP): true, 2552 }, 2553 expectedLocalEndpoints: map[types.NamespacedName]int{ 2554 makeNSN("ns4", "ep4"): 1, 2555 }, 2556 }, { 2557 // Case[14]: change from 0 endpoint address to 1 unnamed port 2558 name: "change from 0 endpoint address to 1 unnamed port", 2559 previousEndpoints: emptyEndpointSlices, 2560 currentEndpoints: namedPort, 2561 oldEndpoints: map[proxy.ServicePortName][]endpointExpectation{}, 2562 expectedResult: map[proxy.ServicePortName][]endpointExpectation{ 2563 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): { 2564 {endpoint: "10.1.1.1:11", isLocal: false}, 2565 }, 2566 }, 2567 expectedDeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 2568 expectedNewlyActiveUDPServices: map[proxy.ServicePortName]bool{ 2569 makeServicePortName("ns1", "ep1", "p11", v1.ProtocolUDP): true, 2570 }, 2571 expectedLocalEndpoints: map[types.NamespacedName]int{}, 2572 }, 2573 } 2574 2575 for tci, tc := range testCases { 2576 t.Run(tc.name, func(t *testing.T) { 2577 _, fp := NewFakeProxier(v1.IPv4Protocol) 2578 fp.hostname = testHostname 2579 2580 // First check that after adding all previous versions of endpoints, 2581 // the fp.oldEndpoints is as we expect. 2582 for i := range tc.previousEndpoints { 2583 if tc.previousEndpoints[i] != nil { 2584 fp.OnEndpointSliceAdd(tc.previousEndpoints[i]) 2585 } 2586 } 2587 fp.endpointsMap.Update(fp.endpointsChanges) 2588 checkEndpointExpectations(t, tci, fp.endpointsMap, tc.oldEndpoints) 2589 2590 // Now let's call appropriate handlers to get to state we want to be. 2591 if len(tc.previousEndpoints) != len(tc.currentEndpoints) { 2592 t.Fatalf("[%d] different lengths of previous and current endpoints", tci) 2593 } 2594 2595 for i := range tc.previousEndpoints { 2596 prev, curr := tc.previousEndpoints[i], tc.currentEndpoints[i] 2597 switch { 2598 case prev == nil: 2599 fp.OnEndpointSliceAdd(curr) 2600 case curr == nil: 2601 fp.OnEndpointSliceDelete(prev) 2602 default: 2603 fp.OnEndpointSliceUpdate(prev, curr) 2604 } 2605 } 2606 result := fp.endpointsMap.Update(fp.endpointsChanges) 2607 newMap := fp.endpointsMap 2608 checkEndpointExpectations(t, tci, newMap, tc.expectedResult) 2609 if len(result.DeletedUDPEndpoints) != len(tc.expectedDeletedUDPEndpoints) { 2610 t.Errorf("[%d] expected %d staleEndpoints, got %d: %v", tci, len(tc.expectedDeletedUDPEndpoints), len(result.DeletedUDPEndpoints), result.DeletedUDPEndpoints) 2611 } 2612 for _, x := range tc.expectedDeletedUDPEndpoints { 2613 found := false 2614 for _, stale := range result.DeletedUDPEndpoints { 2615 if stale == x { 2616 found = true 2617 break 2618 } 2619 } 2620 if !found { 2621 t.Errorf("[%d] expected staleEndpoints[%v], but didn't find it: %v", tci, x, result.DeletedUDPEndpoints) 2622 } 2623 } 2624 if len(result.NewlyActiveUDPServices) != len(tc.expectedNewlyActiveUDPServices) { 2625 t.Errorf("[%d] expected %d staleServiceNames, got %d: %v", tci, len(tc.expectedNewlyActiveUDPServices), len(result.NewlyActiveUDPServices), result.NewlyActiveUDPServices) 2626 } 2627 for svcName := range tc.expectedNewlyActiveUDPServices { 2628 found := false 2629 for _, stale := range result.NewlyActiveUDPServices { 2630 if stale == svcName { 2631 found = true 2632 } 2633 } 2634 if !found { 2635 t.Errorf("[%d] expected staleServiceNames[%v], but didn't find it: %v", tci, svcName, result.NewlyActiveUDPServices) 2636 } 2637 } 2638 localReadyEndpoints := fp.endpointsMap.LocalReadyEndpoints() 2639 if !reflect.DeepEqual(localReadyEndpoints, tc.expectedLocalEndpoints) { 2640 t.Errorf("[%d] expected local endpoints %v, got %v", tci, tc.expectedLocalEndpoints, localReadyEndpoints) 2641 } 2642 }) 2643 } 2644 } 2645 2646 // TestHealthCheckNodePortWhenTerminating tests that health check node ports are not enabled when all local endpoints are terminating 2647 func TestHealthCheckNodePortWhenTerminating(t *testing.T) { 2648 _, fp := NewFakeProxier(v1.IPv4Protocol) 2649 fp.OnServiceSynced() 2650 fp.OnEndpointSlicesSynced() 2651 2652 serviceName := "svc1" 2653 namespaceName := "ns1" 2654 2655 fp.OnServiceAdd(&v1.Service{ 2656 ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName}, 2657 Spec: v1.ServiceSpec{ 2658 ClusterIP: "172.30.1.1", 2659 Selector: map[string]string{"foo": "bar"}, 2660 Ports: []v1.ServicePort{{Name: "", TargetPort: intstr.FromInt32(80), Protocol: v1.ProtocolTCP}}, 2661 }, 2662 }) 2663 2664 endpointSlice := &discovery.EndpointSlice{ 2665 ObjectMeta: metav1.ObjectMeta{ 2666 Name: fmt.Sprintf("%s-1", serviceName), 2667 Namespace: namespaceName, 2668 Labels: map[string]string{discovery.LabelServiceName: serviceName}, 2669 }, 2670 Ports: []discovery.EndpointPort{{ 2671 Name: ptr.To(""), 2672 Port: ptr.To[int32](80), 2673 Protocol: ptr.To(v1.ProtocolTCP), 2674 }}, 2675 AddressType: discovery.AddressTypeIPv4, 2676 Endpoints: []discovery.Endpoint{{ 2677 Addresses: []string{"10.0.1.1"}, 2678 Conditions: discovery.EndpointConditions{Ready: ptr.To(true)}, 2679 NodeName: ptr.To(testHostname), 2680 }, { 2681 Addresses: []string{"10.0.1.2"}, 2682 Conditions: discovery.EndpointConditions{Ready: ptr.To(true)}, 2683 NodeName: ptr.To(testHostname), 2684 }, { 2685 Addresses: []string{"10.0.1.3"}, 2686 Conditions: discovery.EndpointConditions{Ready: ptr.To(true)}, 2687 NodeName: ptr.To(testHostname), 2688 }, { // not ready endpoints should be ignored 2689 Addresses: []string{"10.0.1.4"}, 2690 Conditions: discovery.EndpointConditions{Ready: ptr.To(false)}, 2691 NodeName: ptr.To(testHostname), 2692 }}, 2693 } 2694 2695 fp.OnEndpointSliceAdd(endpointSlice) 2696 _ = fp.endpointsMap.Update(fp.endpointsChanges) 2697 localReadyEndpoints := fp.endpointsMap.LocalReadyEndpoints() 2698 if len(localReadyEndpoints) != 1 { 2699 t.Errorf("unexpected number of local ready endpoints, expected 1 but got: %d", len(localReadyEndpoints)) 2700 } 2701 2702 // set all endpoints to terminating 2703 endpointSliceTerminating := &discovery.EndpointSlice{ 2704 ObjectMeta: metav1.ObjectMeta{ 2705 Name: fmt.Sprintf("%s-1", serviceName), 2706 Namespace: namespaceName, 2707 Labels: map[string]string{discovery.LabelServiceName: serviceName}, 2708 }, 2709 Ports: []discovery.EndpointPort{{ 2710 Name: ptr.To(""), 2711 Port: ptr.To[int32](80), 2712 Protocol: ptr.To(v1.ProtocolTCP), 2713 }}, 2714 AddressType: discovery.AddressTypeIPv4, 2715 Endpoints: []discovery.Endpoint{{ 2716 Addresses: []string{"10.0.1.1"}, 2717 Conditions: discovery.EndpointConditions{ 2718 Ready: ptr.To(false), 2719 Serving: ptr.To(true), 2720 Terminating: ptr.To(false), 2721 }, 2722 NodeName: ptr.To(testHostname), 2723 }, { 2724 Addresses: []string{"10.0.1.2"}, 2725 Conditions: discovery.EndpointConditions{ 2726 Ready: ptr.To(false), 2727 Serving: ptr.To(true), 2728 Terminating: ptr.To(true), 2729 }, 2730 NodeName: ptr.To(testHostname), 2731 }, { 2732 Addresses: []string{"10.0.1.3"}, 2733 Conditions: discovery.EndpointConditions{ 2734 Ready: ptr.To(false), 2735 Serving: ptr.To(true), 2736 Terminating: ptr.To(true), 2737 }, 2738 NodeName: ptr.To(testHostname), 2739 }, { // not ready endpoints should be ignored 2740 Addresses: []string{"10.0.1.4"}, 2741 Conditions: discovery.EndpointConditions{ 2742 Ready: ptr.To(false), 2743 Serving: ptr.To(false), 2744 Terminating: ptr.To(true), 2745 }, 2746 NodeName: ptr.To(testHostname), 2747 }}, 2748 } 2749 2750 fp.OnEndpointSliceUpdate(endpointSlice, endpointSliceTerminating) 2751 _ = fp.endpointsMap.Update(fp.endpointsChanges) 2752 localReadyEndpoints = fp.endpointsMap.LocalReadyEndpoints() 2753 if len(localReadyEndpoints) != 0 { 2754 t.Errorf("unexpected number of local ready endpoints, expected 0 but got: %d", len(localReadyEndpoints)) 2755 } 2756 } 2757 2758 func TestProxierDeleteNodePortStaleUDP(t *testing.T) { 2759 fcmd := fakeexec.FakeCmd{} 2760 fexec := &fakeexec.FakeExec{ 2761 LookPathFunc: func(cmd string) (string, error) { return cmd, nil }, 2762 } 2763 execFunc := func(cmd string, args ...string) exec.Cmd { 2764 return fakeexec.InitFakeCmd(&fcmd, cmd, args...) 2765 } 2766 cmdOutput := "1 flow entries have been deleted" 2767 cmdFunc := func() ([]byte, []byte, error) { return []byte(cmdOutput), nil, nil } 2768 2769 // Delete ClusterIP entries 2770 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc) 2771 fexec.CommandScript = append(fexec.CommandScript, execFunc) 2772 // Delete ExternalIP entries 2773 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc) 2774 fexec.CommandScript = append(fexec.CommandScript, execFunc) 2775 // Delete LoadBalancerIP entries 2776 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc) 2777 fexec.CommandScript = append(fexec.CommandScript, execFunc) 2778 // Delete NodePort entries 2779 fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc) 2780 fexec.CommandScript = append(fexec.CommandScript, execFunc) 2781 2782 _, fp := NewFakeProxier(v1.IPv4Protocol) 2783 fp.exec = fexec 2784 2785 svcIP := "172.30.0.41" 2786 extIP := "192.168.99.11" 2787 lbIngressIP := "1.2.3.4" 2788 svcPort := 80 2789 nodePort := 31201 2790 svcPortName := proxy.ServicePortName{ 2791 NamespacedName: makeNSN("ns1", "svc1"), 2792 Port: "p80", 2793 Protocol: v1.ProtocolUDP, 2794 } 2795 2796 makeServiceMap(fp, 2797 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 2798 svc.Spec.ClusterIP = svcIP 2799 svc.Spec.ExternalIPs = []string{extIP} 2800 svc.Spec.Type = "LoadBalancer" 2801 svc.Spec.Ports = []v1.ServicePort{{ 2802 Name: svcPortName.Port, 2803 Port: int32(svcPort), 2804 Protocol: v1.ProtocolUDP, 2805 NodePort: int32(nodePort), 2806 }} 2807 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 2808 IP: lbIngressIP, 2809 }} 2810 }), 2811 ) 2812 2813 fp.syncProxyRules() 2814 if fexec.CommandCalls != 0 { 2815 t.Fatalf("Created service without endpoints must not clear conntrack entries") 2816 } 2817 2818 epIP := "10.180.0.1" 2819 populateEndpointSlices(fp, 2820 makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) { 2821 eps.AddressType = discovery.AddressTypeIPv4 2822 eps.Endpoints = []discovery.Endpoint{{ 2823 Addresses: []string{epIP}, 2824 Conditions: discovery.EndpointConditions{ 2825 Serving: ptr.To(false), 2826 }, 2827 }} 2828 eps.Ports = []discovery.EndpointPort{{ 2829 Name: ptr.To(svcPortName.Port), 2830 Port: ptr.To(int32(svcPort)), 2831 Protocol: ptr.To(v1.ProtocolUDP), 2832 }} 2833 }), 2834 ) 2835 2836 fp.syncProxyRules() 2837 2838 if fexec.CommandCalls != 0 { 2839 t.Fatalf("Updated UDP service with not ready endpoints must not clear UDP entries") 2840 } 2841 2842 populateEndpointSlices(fp, 2843 makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) { 2844 eps.AddressType = discovery.AddressTypeIPv4 2845 eps.Endpoints = []discovery.Endpoint{{ 2846 Addresses: []string{epIP}, 2847 Conditions: discovery.EndpointConditions{ 2848 Serving: ptr.To(true), 2849 }, 2850 }} 2851 eps.Ports = []discovery.EndpointPort{{ 2852 Name: ptr.To(svcPortName.Port), 2853 Port: ptr.To(int32(svcPort)), 2854 Protocol: ptr.To(v1.ProtocolUDP), 2855 }} 2856 }), 2857 ) 2858 2859 fp.syncProxyRules() 2860 2861 if fexec.CommandCalls != 4 { 2862 t.Fatalf("Updated UDP service with new endpoints must clear UDP entries 4 times: ClusterIP, NodePort, ExternalIP and LB") 2863 } 2864 2865 // the order is not guaranteed so we have to compare the strings in any order 2866 expectedCommands := []string{ 2867 // Delete ClusterIP Conntrack entries 2868 fmt.Sprintf("conntrack -D --orig-dst %s -p %s", svcIP, strings.ToLower(string((v1.ProtocolUDP)))), 2869 // Delete ExternalIP Conntrack entries 2870 fmt.Sprintf("conntrack -D --orig-dst %s -p %s", extIP, strings.ToLower(string((v1.ProtocolUDP)))), 2871 // Delete LoadBalancerIP Conntrack entries 2872 fmt.Sprintf("conntrack -D --orig-dst %s -p %s", lbIngressIP, strings.ToLower(string((v1.ProtocolUDP)))), 2873 // Delete NodePort Conntrack entrie 2874 fmt.Sprintf("conntrack -D -p %s --dport %d", strings.ToLower(string((v1.ProtocolUDP))), nodePort), 2875 } 2876 actualCommands := []string{ 2877 strings.Join(fcmd.CombinedOutputLog[0], " "), 2878 strings.Join(fcmd.CombinedOutputLog[1], " "), 2879 strings.Join(fcmd.CombinedOutputLog[2], " "), 2880 strings.Join(fcmd.CombinedOutputLog[3], " "), 2881 } 2882 sort.Strings(expectedCommands) 2883 sort.Strings(actualCommands) 2884 2885 if !reflect.DeepEqual(expectedCommands, actualCommands) { 2886 t.Errorf("Expected commands: %v, but executed %v", expectedCommands, actualCommands) 2887 } 2888 } 2889 2890 // TODO(thockin): add *more* tests for syncProxyRules() or break it down further and test the pieces. 2891 2892 // This test ensures that the iptables proxier supports translating Endpoints to 2893 // iptables output when internalTrafficPolicy is specified 2894 func TestInternalTrafficPolicy(t *testing.T) { 2895 type endpoint struct { 2896 ip string 2897 hostname string 2898 } 2899 2900 testCases := []struct { 2901 name string 2902 line string 2903 internalTrafficPolicy *v1.ServiceInternalTrafficPolicy 2904 endpoints []endpoint 2905 flowTests []packetFlowTest 2906 }{ 2907 { 2908 name: "internalTrafficPolicy is cluster", 2909 line: getLine(), 2910 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyCluster), 2911 endpoints: []endpoint{ 2912 {"10.0.1.1", testHostname}, 2913 {"10.0.1.2", "host1"}, 2914 {"10.0.1.3", "host2"}, 2915 }, 2916 flowTests: []packetFlowTest{ 2917 { 2918 name: "pod to ClusterIP hits all endpoints", 2919 sourceIP: "10.0.0.2", 2920 destIP: "172.30.1.1", 2921 destPort: 80, 2922 output: "10.0.1.1:80, 10.0.1.2:80, 10.0.1.3:80", 2923 masq: false, 2924 }, 2925 }, 2926 }, 2927 { 2928 name: "internalTrafficPolicy is local and there is one local endpoint", 2929 line: getLine(), 2930 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 2931 endpoints: []endpoint{ 2932 {"10.0.1.1", testHostname}, 2933 {"10.0.1.2", "host1"}, 2934 {"10.0.1.3", "host2"}, 2935 }, 2936 flowTests: []packetFlowTest{ 2937 { 2938 name: "pod to ClusterIP hits only local endpoint", 2939 sourceIP: "10.0.0.2", 2940 destIP: "172.30.1.1", 2941 destPort: 80, 2942 output: "10.0.1.1:80", 2943 masq: false, 2944 }, 2945 }, 2946 }, 2947 { 2948 name: "internalTrafficPolicy is local and there are multiple local endpoints", 2949 line: getLine(), 2950 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 2951 endpoints: []endpoint{ 2952 {"10.0.1.1", testHostname}, 2953 {"10.0.1.2", testHostname}, 2954 {"10.0.1.3", "host2"}, 2955 }, 2956 flowTests: []packetFlowTest{ 2957 { 2958 name: "pod to ClusterIP hits all local endpoints", 2959 sourceIP: "10.0.0.2", 2960 destIP: "172.30.1.1", 2961 destPort: 80, 2962 output: "10.0.1.1:80, 10.0.1.2:80", 2963 masq: false, 2964 }, 2965 }, 2966 }, 2967 { 2968 name: "internalTrafficPolicy is local and there are no local endpoints", 2969 line: getLine(), 2970 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 2971 endpoints: []endpoint{ 2972 {"10.0.1.1", "host0"}, 2973 {"10.0.1.2", "host1"}, 2974 {"10.0.1.3", "host2"}, 2975 }, 2976 flowTests: []packetFlowTest{ 2977 { 2978 name: "no endpoints", 2979 sourceIP: "10.0.0.2", 2980 destIP: "172.30.1.1", 2981 destPort: 80, 2982 output: "DROP", 2983 }, 2984 }, 2985 }, 2986 } 2987 2988 for _, tc := range testCases { 2989 t.Run(tc.name, func(t *testing.T) { 2990 nft, fp := NewFakeProxier(v1.IPv4Protocol) 2991 fp.OnServiceSynced() 2992 fp.OnEndpointSlicesSynced() 2993 2994 serviceName := "svc1" 2995 namespaceName := "ns1" 2996 2997 svc := &v1.Service{ 2998 ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName}, 2999 Spec: v1.ServiceSpec{ 3000 ClusterIP: "172.30.1.1", 3001 Selector: map[string]string{"foo": "bar"}, 3002 Ports: []v1.ServicePort{{Name: "", Port: 80, Protocol: v1.ProtocolTCP}}, 3003 }, 3004 } 3005 if tc.internalTrafficPolicy != nil { 3006 svc.Spec.InternalTrafficPolicy = tc.internalTrafficPolicy 3007 } 3008 3009 fp.OnServiceAdd(svc) 3010 3011 endpointSlice := &discovery.EndpointSlice{ 3012 ObjectMeta: metav1.ObjectMeta{ 3013 Name: fmt.Sprintf("%s-1", serviceName), 3014 Namespace: namespaceName, 3015 Labels: map[string]string{discovery.LabelServiceName: serviceName}, 3016 }, 3017 Ports: []discovery.EndpointPort{{ 3018 Name: ptr.To(""), 3019 Port: ptr.To[int32](80), 3020 Protocol: ptr.To(v1.ProtocolTCP), 3021 }}, 3022 AddressType: discovery.AddressTypeIPv4, 3023 } 3024 for _, ep := range tc.endpoints { 3025 endpointSlice.Endpoints = append(endpointSlice.Endpoints, discovery.Endpoint{ 3026 Addresses: []string{ep.ip}, 3027 Conditions: discovery.EndpointConditions{Ready: ptr.To(true)}, 3028 NodeName: ptr.To(ep.hostname), 3029 }) 3030 } 3031 3032 fp.OnEndpointSliceAdd(endpointSlice) 3033 fp.syncProxyRules() 3034 runPacketFlowTests(t, tc.line, nft, testNodeIPs, tc.flowTests) 3035 3036 fp.OnEndpointSliceDelete(endpointSlice) 3037 fp.syncProxyRules() 3038 runPacketFlowTests(t, tc.line, nft, testNodeIPs, []packetFlowTest{ 3039 { 3040 name: "endpoints deleted", 3041 sourceIP: "10.0.0.2", 3042 destIP: "172.30.1.1", 3043 destPort: 80, 3044 output: "REJECT", 3045 }, 3046 }) 3047 }) 3048 } 3049 } 3050 3051 // TestTerminatingEndpointsTrafficPolicyLocal tests that when there are local ready and 3052 // ready + terminating endpoints, only the ready endpoints are used. 3053 func TestTerminatingEndpointsTrafficPolicyLocal(t *testing.T) { 3054 service := &v1.Service{ 3055 ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}, 3056 Spec: v1.ServiceSpec{ 3057 ClusterIP: "172.30.1.1", 3058 Type: v1.ServiceTypeLoadBalancer, 3059 ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 3060 Ports: []v1.ServicePort{ 3061 { 3062 Name: "", 3063 TargetPort: intstr.FromInt32(80), 3064 Port: 80, 3065 Protocol: v1.ProtocolTCP, 3066 }, 3067 }, 3068 HealthCheckNodePort: 30000, 3069 }, 3070 Status: v1.ServiceStatus{ 3071 LoadBalancer: v1.LoadBalancerStatus{ 3072 Ingress: []v1.LoadBalancerIngress{ 3073 {IP: "1.2.3.4"}, 3074 }, 3075 }, 3076 }, 3077 } 3078 3079 testcases := []struct { 3080 name string 3081 line string 3082 endpointslice *discovery.EndpointSlice 3083 flowTests []packetFlowTest 3084 }{ 3085 { 3086 name: "ready endpoints exist", 3087 line: getLine(), 3088 endpointslice: &discovery.EndpointSlice{ 3089 ObjectMeta: metav1.ObjectMeta{ 3090 Name: fmt.Sprintf("%s-1", "svc1"), 3091 Namespace: "ns1", 3092 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3093 }, 3094 Ports: []discovery.EndpointPort{{ 3095 Name: ptr.To(""), 3096 Port: ptr.To[int32](80), 3097 Protocol: ptr.To(v1.ProtocolTCP), 3098 }}, 3099 AddressType: discovery.AddressTypeIPv4, 3100 Endpoints: []discovery.Endpoint{ 3101 { 3102 Addresses: []string{"10.0.1.1"}, 3103 Conditions: discovery.EndpointConditions{ 3104 Ready: ptr.To(true), 3105 Serving: ptr.To(true), 3106 Terminating: ptr.To(false), 3107 }, 3108 NodeName: ptr.To(testHostname), 3109 }, 3110 { 3111 Addresses: []string{"10.0.1.2"}, 3112 Conditions: discovery.EndpointConditions{ 3113 Ready: ptr.To(true), 3114 Serving: ptr.To(true), 3115 Terminating: ptr.To(false), 3116 }, 3117 NodeName: ptr.To(testHostname), 3118 }, 3119 { 3120 // this endpoint should be ignored for external since there are ready non-terminating endpoints 3121 Addresses: []string{"10.0.1.3"}, 3122 Conditions: discovery.EndpointConditions{ 3123 Ready: ptr.To(false), 3124 Serving: ptr.To(true), 3125 Terminating: ptr.To(true), 3126 }, 3127 NodeName: ptr.To(testHostname), 3128 }, 3129 { 3130 // this endpoint should be ignored for external since there are ready non-terminating endpoints 3131 Addresses: []string{"10.0.1.4"}, 3132 Conditions: discovery.EndpointConditions{ 3133 Ready: ptr.To(false), 3134 Serving: ptr.To(false), 3135 Terminating: ptr.To(true), 3136 }, 3137 NodeName: ptr.To(testHostname), 3138 }, 3139 { 3140 // this endpoint should be ignored for external since it's not local 3141 Addresses: []string{"10.0.1.5"}, 3142 Conditions: discovery.EndpointConditions{ 3143 Ready: ptr.To(true), 3144 Serving: ptr.To(true), 3145 Terminating: ptr.To(false), 3146 }, 3147 NodeName: ptr.To("host-1"), 3148 }, 3149 }, 3150 }, 3151 flowTests: []packetFlowTest{ 3152 { 3153 name: "pod to clusterIP", 3154 sourceIP: "10.0.0.2", 3155 destIP: "172.30.1.1", 3156 destPort: 80, 3157 output: "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80", 3158 masq: false, 3159 }, 3160 { 3161 name: "external to LB", 3162 sourceIP: testExternalClient, 3163 destIP: "1.2.3.4", 3164 destPort: 80, 3165 output: "10.0.1.1:80, 10.0.1.2:80", 3166 masq: false, 3167 }, 3168 }, 3169 }, 3170 { 3171 name: "only terminating endpoints exist", 3172 line: getLine(), 3173 endpointslice: &discovery.EndpointSlice{ 3174 ObjectMeta: metav1.ObjectMeta{ 3175 Name: fmt.Sprintf("%s-1", "svc1"), 3176 Namespace: "ns1", 3177 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3178 }, 3179 Ports: []discovery.EndpointPort{{ 3180 Name: ptr.To(""), 3181 Port: ptr.To[int32](80), 3182 Protocol: ptr.To(v1.ProtocolTCP), 3183 }}, 3184 AddressType: discovery.AddressTypeIPv4, 3185 Endpoints: []discovery.Endpoint{ 3186 { 3187 // this endpoint should be used since there are only ready terminating endpoints 3188 Addresses: []string{"10.0.1.2"}, 3189 Conditions: discovery.EndpointConditions{ 3190 Ready: ptr.To(false), 3191 Serving: ptr.To(true), 3192 Terminating: ptr.To(true), 3193 }, 3194 NodeName: ptr.To(testHostname), 3195 }, 3196 { 3197 // this endpoint should be used since there are only ready terminating endpoints 3198 Addresses: []string{"10.0.1.3"}, 3199 Conditions: discovery.EndpointConditions{ 3200 Ready: ptr.To(false), 3201 Serving: ptr.To(true), 3202 Terminating: ptr.To(true), 3203 }, 3204 NodeName: ptr.To(testHostname), 3205 }, 3206 { 3207 // this endpoint should not be used since it is both terminating and not ready. 3208 Addresses: []string{"10.0.1.4"}, 3209 Conditions: discovery.EndpointConditions{ 3210 Ready: ptr.To(false), 3211 Serving: ptr.To(false), 3212 Terminating: ptr.To(true), 3213 }, 3214 NodeName: ptr.To(testHostname), 3215 }, 3216 { 3217 // this endpoint should be ignored for external since it's not local 3218 Addresses: []string{"10.0.1.5"}, 3219 Conditions: discovery.EndpointConditions{ 3220 Ready: ptr.To(true), 3221 Serving: ptr.To(true), 3222 Terminating: ptr.To(false), 3223 }, 3224 NodeName: ptr.To("host-1"), 3225 }, 3226 }, 3227 }, 3228 flowTests: []packetFlowTest{ 3229 { 3230 name: "pod to clusterIP", 3231 sourceIP: "10.0.0.2", 3232 destIP: "172.30.1.1", 3233 destPort: 80, 3234 output: "10.0.1.5:80", 3235 masq: false, 3236 }, 3237 { 3238 name: "external to LB", 3239 sourceIP: testExternalClient, 3240 destIP: "1.2.3.4", 3241 destPort: 80, 3242 output: "10.0.1.2:80, 10.0.1.3:80", 3243 masq: false, 3244 }, 3245 }, 3246 }, 3247 { 3248 name: "terminating endpoints on remote node", 3249 line: getLine(), 3250 endpointslice: &discovery.EndpointSlice{ 3251 ObjectMeta: metav1.ObjectMeta{ 3252 Name: fmt.Sprintf("%s-1", "svc1"), 3253 Namespace: "ns1", 3254 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3255 }, 3256 Ports: []discovery.EndpointPort{{ 3257 Name: ptr.To(""), 3258 Port: ptr.To[int32](80), 3259 Protocol: ptr.To(v1.ProtocolTCP), 3260 }}, 3261 AddressType: discovery.AddressTypeIPv4, 3262 Endpoints: []discovery.Endpoint{ 3263 { 3264 // this endpoint won't be used because it's not local, 3265 // but it will prevent a REJECT rule from being created 3266 Addresses: []string{"10.0.1.5"}, 3267 Conditions: discovery.EndpointConditions{ 3268 Ready: ptr.To(false), 3269 Serving: ptr.To(true), 3270 Terminating: ptr.To(true), 3271 }, 3272 NodeName: ptr.To("host-1"), 3273 }, 3274 }, 3275 }, 3276 flowTests: []packetFlowTest{ 3277 { 3278 name: "pod to clusterIP", 3279 sourceIP: "10.0.0.2", 3280 destIP: "172.30.1.1", 3281 destPort: 80, 3282 output: "10.0.1.5:80", 3283 }, 3284 { 3285 name: "external to LB, no locally-usable endpoints", 3286 sourceIP: testExternalClient, 3287 destIP: "1.2.3.4", 3288 destPort: 80, 3289 output: "DROP", 3290 }, 3291 }, 3292 }, 3293 { 3294 name: "no usable endpoints on any node", 3295 line: getLine(), 3296 endpointslice: &discovery.EndpointSlice{ 3297 ObjectMeta: metav1.ObjectMeta{ 3298 Name: fmt.Sprintf("%s-1", "svc1"), 3299 Namespace: "ns1", 3300 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3301 }, 3302 Ports: []discovery.EndpointPort{{ 3303 Name: ptr.To(""), 3304 Port: ptr.To[int32](80), 3305 Protocol: ptr.To(v1.ProtocolTCP), 3306 }}, 3307 AddressType: discovery.AddressTypeIPv4, 3308 Endpoints: []discovery.Endpoint{ 3309 { 3310 // Local but not ready or serving 3311 Addresses: []string{"10.0.1.5"}, 3312 Conditions: discovery.EndpointConditions{ 3313 Ready: ptr.To(false), 3314 Serving: ptr.To(false), 3315 Terminating: ptr.To(true), 3316 }, 3317 NodeName: ptr.To(testHostname), 3318 }, 3319 { 3320 // Remote and not ready or serving 3321 Addresses: []string{"10.0.1.5"}, 3322 Conditions: discovery.EndpointConditions{ 3323 Ready: ptr.To(false), 3324 Serving: ptr.To(false), 3325 Terminating: ptr.To(true), 3326 }, 3327 NodeName: ptr.To("host-1"), 3328 }, 3329 }, 3330 }, 3331 flowTests: []packetFlowTest{ 3332 { 3333 name: "pod to clusterIP, no usable endpoints", 3334 sourceIP: "10.0.0.2", 3335 destIP: "172.30.1.1", 3336 destPort: 80, 3337 output: "REJECT", 3338 }, 3339 { 3340 name: "external to LB, no usable endpoints", 3341 sourceIP: testExternalClient, 3342 destIP: "1.2.3.4", 3343 destPort: 80, 3344 output: "REJECT", 3345 }, 3346 }, 3347 }, 3348 } 3349 3350 for _, testcase := range testcases { 3351 t.Run(testcase.name, func(t *testing.T) { 3352 nft, fp := NewFakeProxier(v1.IPv4Protocol) 3353 fp.OnServiceSynced() 3354 fp.OnEndpointSlicesSynced() 3355 3356 fp.OnServiceAdd(service) 3357 3358 fp.OnEndpointSliceAdd(testcase.endpointslice) 3359 fp.syncProxyRules() 3360 runPacketFlowTests(t, testcase.line, nft, testNodeIPs, testcase.flowTests) 3361 3362 fp.OnEndpointSliceDelete(testcase.endpointslice) 3363 fp.syncProxyRules() 3364 runPacketFlowTests(t, testcase.line, nft, testNodeIPs, []packetFlowTest{ 3365 { 3366 name: "pod to clusterIP after endpoints deleted", 3367 sourceIP: "10.0.0.2", 3368 destIP: "172.30.1.1", 3369 destPort: 80, 3370 output: "REJECT", 3371 }, 3372 { 3373 name: "external to LB after endpoints deleted", 3374 sourceIP: testExternalClient, 3375 destIP: "1.2.3.4", 3376 destPort: 80, 3377 output: "REJECT", 3378 }, 3379 }) 3380 }) 3381 } 3382 } 3383 3384 // TestTerminatingEndpointsTrafficPolicyCluster tests that when there are cluster-wide 3385 // ready and ready + terminating endpoints, only the ready endpoints are used. 3386 func TestTerminatingEndpointsTrafficPolicyCluster(t *testing.T) { 3387 service := &v1.Service{ 3388 ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}, 3389 Spec: v1.ServiceSpec{ 3390 ClusterIP: "172.30.1.1", 3391 Type: v1.ServiceTypeLoadBalancer, 3392 ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyCluster, 3393 Ports: []v1.ServicePort{ 3394 { 3395 Name: "", 3396 TargetPort: intstr.FromInt32(80), 3397 Port: 80, 3398 Protocol: v1.ProtocolTCP, 3399 }, 3400 }, 3401 HealthCheckNodePort: 30000, 3402 }, 3403 Status: v1.ServiceStatus{ 3404 LoadBalancer: v1.LoadBalancerStatus{ 3405 Ingress: []v1.LoadBalancerIngress{ 3406 {IP: "1.2.3.4"}, 3407 }, 3408 }, 3409 }, 3410 } 3411 3412 testcases := []struct { 3413 name string 3414 line string 3415 endpointslice *discovery.EndpointSlice 3416 flowTests []packetFlowTest 3417 }{ 3418 { 3419 name: "ready endpoints exist", 3420 line: getLine(), 3421 endpointslice: &discovery.EndpointSlice{ 3422 ObjectMeta: metav1.ObjectMeta{ 3423 Name: fmt.Sprintf("%s-1", "svc1"), 3424 Namespace: "ns1", 3425 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3426 }, 3427 Ports: []discovery.EndpointPort{{ 3428 Name: ptr.To(""), 3429 Port: ptr.To[int32](80), 3430 Protocol: ptr.To(v1.ProtocolTCP), 3431 }}, 3432 AddressType: discovery.AddressTypeIPv4, 3433 Endpoints: []discovery.Endpoint{ 3434 { 3435 Addresses: []string{"10.0.1.1"}, 3436 Conditions: discovery.EndpointConditions{ 3437 Ready: ptr.To(true), 3438 Serving: ptr.To(true), 3439 Terminating: ptr.To(false), 3440 }, 3441 NodeName: ptr.To(testHostname), 3442 }, 3443 { 3444 Addresses: []string{"10.0.1.2"}, 3445 Conditions: discovery.EndpointConditions{ 3446 Ready: ptr.To(true), 3447 Serving: ptr.To(true), 3448 Terminating: ptr.To(false), 3449 }, 3450 NodeName: ptr.To(testHostname), 3451 }, 3452 { 3453 // this endpoint should be ignored since there are ready non-terminating endpoints 3454 Addresses: []string{"10.0.1.3"}, 3455 Conditions: discovery.EndpointConditions{ 3456 Ready: ptr.To(false), 3457 Serving: ptr.To(true), 3458 Terminating: ptr.To(true), 3459 }, 3460 NodeName: ptr.To("another-host"), 3461 }, 3462 { 3463 // this endpoint should be ignored since it is not "serving" 3464 Addresses: []string{"10.0.1.4"}, 3465 Conditions: discovery.EndpointConditions{ 3466 Ready: ptr.To(false), 3467 Serving: ptr.To(false), 3468 Terminating: ptr.To(true), 3469 }, 3470 NodeName: ptr.To("another-host"), 3471 }, 3472 { 3473 Addresses: []string{"10.0.1.5"}, 3474 Conditions: discovery.EndpointConditions{ 3475 Ready: ptr.To(true), 3476 Serving: ptr.To(true), 3477 Terminating: ptr.To(false), 3478 }, 3479 NodeName: ptr.To("another-host"), 3480 }, 3481 }, 3482 }, 3483 flowTests: []packetFlowTest{ 3484 { 3485 name: "pod to clusterIP", 3486 sourceIP: "10.0.0.2", 3487 destIP: "172.30.1.1", 3488 destPort: 80, 3489 output: "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80", 3490 masq: false, 3491 }, 3492 { 3493 name: "external to LB", 3494 sourceIP: testExternalClient, 3495 destIP: "1.2.3.4", 3496 destPort: 80, 3497 output: "10.0.1.1:80, 10.0.1.2:80, 10.0.1.5:80", 3498 masq: true, 3499 }, 3500 }, 3501 }, 3502 { 3503 name: "only terminating endpoints exist", 3504 line: getLine(), 3505 endpointslice: &discovery.EndpointSlice{ 3506 ObjectMeta: metav1.ObjectMeta{ 3507 Name: fmt.Sprintf("%s-1", "svc1"), 3508 Namespace: "ns1", 3509 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3510 }, 3511 Ports: []discovery.EndpointPort{{ 3512 Name: ptr.To(""), 3513 Port: ptr.To[int32](80), 3514 Protocol: ptr.To(v1.ProtocolTCP), 3515 }}, 3516 AddressType: discovery.AddressTypeIPv4, 3517 Endpoints: []discovery.Endpoint{ 3518 { 3519 // this endpoint should be used since there are only ready terminating endpoints 3520 Addresses: []string{"10.0.1.2"}, 3521 Conditions: discovery.EndpointConditions{ 3522 Ready: ptr.To(false), 3523 Serving: ptr.To(true), 3524 Terminating: ptr.To(true), 3525 }, 3526 NodeName: ptr.To(testHostname), 3527 }, 3528 { 3529 // this endpoint should be used since there are only ready terminating endpoints 3530 Addresses: []string{"10.0.1.3"}, 3531 Conditions: discovery.EndpointConditions{ 3532 Ready: ptr.To(false), 3533 Serving: ptr.To(true), 3534 Terminating: ptr.To(true), 3535 }, 3536 NodeName: ptr.To(testHostname), 3537 }, 3538 { 3539 // this endpoint should not be used since it is both terminating and not ready. 3540 Addresses: []string{"10.0.1.4"}, 3541 Conditions: discovery.EndpointConditions{ 3542 Ready: ptr.To(false), 3543 Serving: ptr.To(false), 3544 Terminating: ptr.To(true), 3545 }, 3546 NodeName: ptr.To("another-host"), 3547 }, 3548 { 3549 // this endpoint should be used since there are only ready terminating endpoints 3550 Addresses: []string{"10.0.1.5"}, 3551 Conditions: discovery.EndpointConditions{ 3552 Ready: ptr.To(false), 3553 Serving: ptr.To(true), 3554 Terminating: ptr.To(true), 3555 }, 3556 NodeName: ptr.To("another-host"), 3557 }, 3558 }, 3559 }, 3560 flowTests: []packetFlowTest{ 3561 { 3562 name: "pod to clusterIP", 3563 sourceIP: "10.0.0.2", 3564 destIP: "172.30.1.1", 3565 destPort: 80, 3566 output: "10.0.1.2:80, 10.0.1.3:80, 10.0.1.5:80", 3567 masq: false, 3568 }, 3569 { 3570 name: "external to LB", 3571 sourceIP: testExternalClient, 3572 destIP: "1.2.3.4", 3573 destPort: 80, 3574 output: "10.0.1.2:80, 10.0.1.3:80, 10.0.1.5:80", 3575 masq: true, 3576 }, 3577 }, 3578 }, 3579 { 3580 name: "terminating endpoints on remote node", 3581 line: getLine(), 3582 endpointslice: &discovery.EndpointSlice{ 3583 ObjectMeta: metav1.ObjectMeta{ 3584 Name: fmt.Sprintf("%s-1", "svc1"), 3585 Namespace: "ns1", 3586 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3587 }, 3588 Ports: []discovery.EndpointPort{{ 3589 Name: ptr.To(""), 3590 Port: ptr.To[int32](80), 3591 Protocol: ptr.To(v1.ProtocolTCP), 3592 }}, 3593 AddressType: discovery.AddressTypeIPv4, 3594 Endpoints: []discovery.Endpoint{ 3595 { 3596 Addresses: []string{"10.0.1.5"}, 3597 Conditions: discovery.EndpointConditions{ 3598 Ready: ptr.To(false), 3599 Serving: ptr.To(true), 3600 Terminating: ptr.To(true), 3601 }, 3602 NodeName: ptr.To("host-1"), 3603 }, 3604 }, 3605 }, 3606 flowTests: []packetFlowTest{ 3607 { 3608 name: "pod to clusterIP", 3609 sourceIP: "10.0.0.2", 3610 destIP: "172.30.1.1", 3611 destPort: 80, 3612 output: "10.0.1.5:80", 3613 masq: false, 3614 }, 3615 { 3616 name: "external to LB", 3617 sourceIP: testExternalClient, 3618 destIP: "1.2.3.4", 3619 destPort: 80, 3620 output: "10.0.1.5:80", 3621 masq: true, 3622 }, 3623 }, 3624 }, 3625 { 3626 name: "no usable endpoints on any node", 3627 line: getLine(), 3628 endpointslice: &discovery.EndpointSlice{ 3629 ObjectMeta: metav1.ObjectMeta{ 3630 Name: fmt.Sprintf("%s-1", "svc1"), 3631 Namespace: "ns1", 3632 Labels: map[string]string{discovery.LabelServiceName: "svc1"}, 3633 }, 3634 Ports: []discovery.EndpointPort{{ 3635 Name: ptr.To(""), 3636 Port: ptr.To[int32](80), 3637 Protocol: ptr.To(v1.ProtocolTCP), 3638 }}, 3639 AddressType: discovery.AddressTypeIPv4, 3640 Endpoints: []discovery.Endpoint{ 3641 { 3642 // Local, not ready or serving 3643 Addresses: []string{"10.0.1.5"}, 3644 Conditions: discovery.EndpointConditions{ 3645 Ready: ptr.To(false), 3646 Serving: ptr.To(false), 3647 Terminating: ptr.To(true), 3648 }, 3649 NodeName: ptr.To(testHostname), 3650 }, 3651 { 3652 // Remote, not ready or serving 3653 Addresses: []string{"10.0.1.5"}, 3654 Conditions: discovery.EndpointConditions{ 3655 Ready: ptr.To(false), 3656 Serving: ptr.To(false), 3657 Terminating: ptr.To(true), 3658 }, 3659 NodeName: ptr.To("host-1"), 3660 }, 3661 }, 3662 }, 3663 flowTests: []packetFlowTest{ 3664 { 3665 name: "pod to clusterIP", 3666 sourceIP: "10.0.0.2", 3667 destIP: "172.30.1.1", 3668 destPort: 80, 3669 output: "REJECT", 3670 }, 3671 { 3672 name: "external to LB", 3673 sourceIP: testExternalClient, 3674 destIP: "1.2.3.4", 3675 destPort: 80, 3676 output: "REJECT", 3677 }, 3678 }, 3679 }, 3680 } 3681 3682 for _, testcase := range testcases { 3683 t.Run(testcase.name, func(t *testing.T) { 3684 3685 nft, fp := NewFakeProxier(v1.IPv4Protocol) 3686 fp.OnServiceSynced() 3687 fp.OnEndpointSlicesSynced() 3688 3689 fp.OnServiceAdd(service) 3690 3691 fp.OnEndpointSliceAdd(testcase.endpointslice) 3692 fp.syncProxyRules() 3693 runPacketFlowTests(t, testcase.line, nft, testNodeIPs, testcase.flowTests) 3694 3695 fp.OnEndpointSliceDelete(testcase.endpointslice) 3696 fp.syncProxyRules() 3697 runPacketFlowTests(t, testcase.line, nft, testNodeIPs, []packetFlowTest{ 3698 { 3699 name: "pod to clusterIP after endpoints deleted", 3700 sourceIP: "10.0.0.2", 3701 destIP: "172.30.1.1", 3702 destPort: 80, 3703 output: "REJECT", 3704 }, 3705 { 3706 name: "external to LB after endpoints deleted", 3707 sourceIP: testExternalClient, 3708 destIP: "1.2.3.4", 3709 destPort: 80, 3710 output: "REJECT", 3711 }, 3712 }) 3713 }) 3714 } 3715 } 3716 3717 func TestInternalExternalMasquerade(t *testing.T) { 3718 // (Put the test setup code in an internal function so we can have it here at the 3719 // top, before the test cases that will be run against it.) 3720 setupTest := func(fp *Proxier) { 3721 makeServiceMap(fp, 3722 makeTestService("ns1", "svc1", func(svc *v1.Service) { 3723 svc.Spec.Type = "LoadBalancer" 3724 svc.Spec.ClusterIP = "172.30.0.41" 3725 svc.Spec.Ports = []v1.ServicePort{{ 3726 Name: "p80", 3727 Port: 80, 3728 Protocol: v1.ProtocolTCP, 3729 NodePort: int32(3001), 3730 }} 3731 svc.Spec.HealthCheckNodePort = 30001 3732 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 3733 IP: "1.2.3.4", 3734 }} 3735 }), 3736 makeTestService("ns2", "svc2", func(svc *v1.Service) { 3737 svc.Spec.Type = "LoadBalancer" 3738 svc.Spec.ClusterIP = "172.30.0.42" 3739 svc.Spec.Ports = []v1.ServicePort{{ 3740 Name: "p80", 3741 Port: 80, 3742 Protocol: v1.ProtocolTCP, 3743 NodePort: int32(3002), 3744 }} 3745 svc.Spec.HealthCheckNodePort = 30002 3746 svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyLocal 3747 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 3748 IP: "5.6.7.8", 3749 }} 3750 }), 3751 makeTestService("ns3", "svc3", func(svc *v1.Service) { 3752 svc.Spec.Type = "LoadBalancer" 3753 svc.Spec.ClusterIP = "172.30.0.43" 3754 svc.Spec.Ports = []v1.ServicePort{{ 3755 Name: "p80", 3756 Port: 80, 3757 Protocol: v1.ProtocolTCP, 3758 NodePort: int32(3003), 3759 }} 3760 svc.Spec.HealthCheckNodePort = 30003 3761 svc.Spec.InternalTrafficPolicy = ptr.To(v1.ServiceInternalTrafficPolicyLocal) 3762 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 3763 IP: "9.10.11.12", 3764 }} 3765 }), 3766 ) 3767 3768 populateEndpointSlices(fp, 3769 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 3770 eps.AddressType = discovery.AddressTypeIPv4 3771 eps.Endpoints = []discovery.Endpoint{ 3772 { 3773 Addresses: []string{"10.180.0.1"}, 3774 NodeName: ptr.To(testHostname), 3775 }, 3776 { 3777 Addresses: []string{"10.180.1.1"}, 3778 NodeName: ptr.To("remote"), 3779 }, 3780 } 3781 eps.Ports = []discovery.EndpointPort{{ 3782 Name: ptr.To("p80"), 3783 Port: ptr.To[int32](80), 3784 Protocol: ptr.To(v1.ProtocolTCP), 3785 }} 3786 }), 3787 makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) { 3788 eps.AddressType = discovery.AddressTypeIPv4 3789 eps.Endpoints = []discovery.Endpoint{ 3790 { 3791 Addresses: []string{"10.180.0.2"}, 3792 NodeName: ptr.To(testHostname), 3793 }, 3794 { 3795 Addresses: []string{"10.180.1.2"}, 3796 NodeName: ptr.To("remote"), 3797 }, 3798 } 3799 eps.Ports = []discovery.EndpointPort{{ 3800 Name: ptr.To("p80"), 3801 Port: ptr.To[int32](80), 3802 Protocol: ptr.To(v1.ProtocolTCP), 3803 }} 3804 }), 3805 makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) { 3806 eps.AddressType = discovery.AddressTypeIPv4 3807 eps.Endpoints = []discovery.Endpoint{ 3808 { 3809 Addresses: []string{"10.180.0.3"}, 3810 NodeName: ptr.To(testHostname), 3811 }, 3812 { 3813 Addresses: []string{"10.180.1.3"}, 3814 NodeName: ptr.To("remote"), 3815 }, 3816 } 3817 eps.Ports = []discovery.EndpointPort{{ 3818 Name: ptr.To("p80"), 3819 Port: ptr.To[int32](80), 3820 Protocol: ptr.To(v1.ProtocolTCP), 3821 }} 3822 }), 3823 ) 3824 3825 fp.syncProxyRules() 3826 } 3827 3828 // We use the same flowTests for all of the testCases. The "output" and "masq" 3829 // values here represent the normal case (working localDetector, no masqueradeAll) 3830 flowTests := []packetFlowTest{ 3831 { 3832 name: "pod to ClusterIP", 3833 sourceIP: "10.0.0.2", 3834 destIP: "172.30.0.41", 3835 destPort: 80, 3836 output: "10.180.0.1:80, 10.180.1.1:80", 3837 masq: false, 3838 }, 3839 { 3840 name: "pod to NodePort", 3841 sourceIP: "10.0.0.2", 3842 destIP: testNodeIP, 3843 destPort: 3001, 3844 output: "10.180.0.1:80, 10.180.1.1:80", 3845 masq: true, 3846 }, 3847 { 3848 name: "pod to LB", 3849 sourceIP: "10.0.0.2", 3850 destIP: "1.2.3.4", 3851 destPort: 80, 3852 output: "10.180.0.1:80, 10.180.1.1:80", 3853 masq: true, 3854 }, 3855 { 3856 name: "node to ClusterIP", 3857 sourceIP: testNodeIP, 3858 destIP: "172.30.0.41", 3859 destPort: 80, 3860 output: "10.180.0.1:80, 10.180.1.1:80", 3861 masq: true, 3862 }, 3863 { 3864 name: "node to NodePort", 3865 sourceIP: testNodeIP, 3866 destIP: testNodeIP, 3867 destPort: 3001, 3868 output: "10.180.0.1:80, 10.180.1.1:80", 3869 masq: true, 3870 }, 3871 { 3872 name: "node to LB", 3873 sourceIP: testNodeIP, 3874 destIP: "1.2.3.4", 3875 destPort: 80, 3876 output: "10.180.0.1:80, 10.180.1.1:80", 3877 masq: true, 3878 }, 3879 { 3880 name: "external to ClusterIP", 3881 sourceIP: testExternalClient, 3882 destIP: "172.30.0.41", 3883 destPort: 80, 3884 output: "10.180.0.1:80, 10.180.1.1:80", 3885 masq: true, 3886 }, 3887 { 3888 name: "external to NodePort", 3889 sourceIP: testExternalClient, 3890 destIP: testNodeIP, 3891 destPort: 3001, 3892 output: "10.180.0.1:80, 10.180.1.1:80", 3893 masq: true, 3894 }, 3895 { 3896 name: "external to LB", 3897 sourceIP: testExternalClient, 3898 destIP: "1.2.3.4", 3899 destPort: 80, 3900 output: "10.180.0.1:80, 10.180.1.1:80", 3901 masq: true, 3902 }, 3903 { 3904 name: "pod to ClusterIP with eTP:Local", 3905 sourceIP: "10.0.0.2", 3906 destIP: "172.30.0.42", 3907 destPort: 80, 3908 3909 // externalTrafficPolicy does not apply to ClusterIP traffic, so same 3910 // as "Pod to ClusterIP" 3911 output: "10.180.0.2:80, 10.180.1.2:80", 3912 masq: false, 3913 }, 3914 { 3915 name: "pod to NodePort with eTP:Local", 3916 sourceIP: "10.0.0.2", 3917 destIP: testNodeIP, 3918 destPort: 3002, 3919 3920 // See the comment below in the "pod to LB with eTP:Local" case. 3921 // It doesn't actually make sense to short-circuit here, since if 3922 // you connect directly to a NodePort from outside the cluster, 3923 // you only get the local endpoints. But it's simpler for us and 3924 // slightly more convenient for users to have this case get 3925 // short-circuited too. 3926 output: "10.180.0.2:80, 10.180.1.2:80", 3927 masq: false, 3928 }, 3929 { 3930 name: "pod to LB with eTP:Local", 3931 sourceIP: "10.0.0.2", 3932 destIP: "5.6.7.8", 3933 destPort: 80, 3934 3935 // The short-circuit rule is supposed to make this behave the same 3936 // way it would if the packet actually went out to the LB and then 3937 // came back into the cluster. So it gets routed to all endpoints, 3938 // not just local ones. In reality, if the packet actually left 3939 // the cluster, it would have to get masqueraded, but since we can 3940 // avoid doing that in the short-circuit case, and not masquerading 3941 // is more useful, we avoid masquerading. 3942 output: "10.180.0.2:80, 10.180.1.2:80", 3943 masq: false, 3944 }, 3945 { 3946 name: "node to ClusterIP with eTP:Local", 3947 sourceIP: testNodeIP, 3948 destIP: "172.30.0.42", 3949 destPort: 80, 3950 3951 // externalTrafficPolicy does not apply to ClusterIP traffic, so same 3952 // as "node to ClusterIP" 3953 output: "10.180.0.2:80, 10.180.1.2:80", 3954 masq: true, 3955 }, 3956 { 3957 name: "node to NodePort with eTP:Local", 3958 sourceIP: testNodeIP, 3959 destIP: testNodeIP, 3960 destPort: 3001, 3961 3962 // The traffic gets short-circuited, ignoring externalTrafficPolicy, so 3963 // same as "node to NodePort" above. 3964 output: "10.180.0.1:80, 10.180.1.1:80", 3965 masq: true, 3966 }, 3967 { 3968 name: "node to LB with eTP:Local", 3969 sourceIP: testNodeIP, 3970 destIP: "5.6.7.8", 3971 destPort: 80, 3972 3973 // The traffic gets short-circuited, ignoring externalTrafficPolicy, so 3974 // same as "node to LB" above. 3975 output: "10.180.0.2:80, 10.180.1.2:80", 3976 masq: true, 3977 }, 3978 { 3979 name: "external to ClusterIP with eTP:Local", 3980 sourceIP: testExternalClient, 3981 destIP: "172.30.0.42", 3982 destPort: 80, 3983 3984 // externalTrafficPolicy does not apply to ClusterIP traffic, so same 3985 // as "external to ClusterIP" above. 3986 output: "10.180.0.2:80, 10.180.1.2:80", 3987 masq: true, 3988 }, 3989 { 3990 name: "external to NodePort with eTP:Local", 3991 sourceIP: testExternalClient, 3992 destIP: testNodeIP, 3993 destPort: 3002, 3994 3995 // externalTrafficPolicy applies; only the local endpoint is 3996 // selected, and we don't masquerade. 3997 output: "10.180.0.2:80", 3998 masq: false, 3999 }, 4000 { 4001 name: "external to LB with eTP:Local", 4002 sourceIP: testExternalClient, 4003 destIP: "5.6.7.8", 4004 destPort: 80, 4005 4006 // externalTrafficPolicy applies; only the local endpoint is 4007 // selected, and we don't masquerade. 4008 output: "10.180.0.2:80", 4009 masq: false, 4010 }, 4011 { 4012 name: "pod to ClusterIP with iTP:Local", 4013 sourceIP: "10.0.0.2", 4014 destIP: "172.30.0.43", 4015 destPort: 80, 4016 4017 // internalTrafficPolicy applies; only the local endpoint is 4018 // selected. 4019 output: "10.180.0.3:80", 4020 masq: false, 4021 }, 4022 { 4023 name: "pod to NodePort with iTP:Local", 4024 sourceIP: "10.0.0.2", 4025 destIP: testNodeIP, 4026 destPort: 3003, 4027 4028 // internalTrafficPolicy does not apply to NodePort traffic, so same as 4029 // "pod to NodePort" above. 4030 output: "10.180.0.3:80, 10.180.1.3:80", 4031 masq: true, 4032 }, 4033 { 4034 name: "pod to LB with iTP:Local", 4035 sourceIP: "10.0.0.2", 4036 destIP: "9.10.11.12", 4037 destPort: 80, 4038 4039 // internalTrafficPolicy does not apply to LoadBalancer traffic, so 4040 // same as "pod to LB" above. 4041 output: "10.180.0.3:80, 10.180.1.3:80", 4042 masq: true, 4043 }, 4044 { 4045 name: "node to ClusterIP with iTP:Local", 4046 sourceIP: testNodeIP, 4047 destIP: "172.30.0.43", 4048 destPort: 80, 4049 4050 // internalTrafficPolicy applies; only the local endpoint is selected. 4051 // Traffic is masqueraded as in the "node to ClusterIP" case because 4052 // internalTrafficPolicy does not affect masquerading. 4053 output: "10.180.0.3:80", 4054 masq: true, 4055 }, 4056 { 4057 name: "node to NodePort with iTP:Local", 4058 sourceIP: testNodeIP, 4059 destIP: testNodeIP, 4060 destPort: 3003, 4061 4062 // internalTrafficPolicy does not apply to NodePort traffic, so same as 4063 // "node to NodePort" above. 4064 output: "10.180.0.3:80, 10.180.1.3:80", 4065 masq: true, 4066 }, 4067 { 4068 name: "node to LB with iTP:Local", 4069 sourceIP: testNodeIP, 4070 destIP: "9.10.11.12", 4071 destPort: 80, 4072 4073 // internalTrafficPolicy does not apply to LoadBalancer traffic, so 4074 // same as "node to LB" above. 4075 output: "10.180.0.3:80, 10.180.1.3:80", 4076 masq: true, 4077 }, 4078 { 4079 name: "external to ClusterIP with iTP:Local", 4080 sourceIP: testExternalClient, 4081 destIP: "172.30.0.43", 4082 destPort: 80, 4083 4084 // internalTrafficPolicy applies; only the local endpoint is selected. 4085 // Traffic is masqueraded as in the "external to ClusterIP" case 4086 // because internalTrafficPolicy does not affect masquerading. 4087 output: "10.180.0.3:80", 4088 masq: true, 4089 }, 4090 { 4091 name: "external to NodePort with iTP:Local", 4092 sourceIP: testExternalClient, 4093 destIP: testNodeIP, 4094 destPort: 3003, 4095 4096 // internalTrafficPolicy does not apply to NodePort traffic, so same as 4097 // "external to NodePort" above. 4098 output: "10.180.0.3:80, 10.180.1.3:80", 4099 masq: true, 4100 }, 4101 { 4102 name: "external to LB with iTP:Local", 4103 sourceIP: testExternalClient, 4104 destIP: "9.10.11.12", 4105 destPort: 80, 4106 4107 // internalTrafficPolicy does not apply to LoadBalancer traffic, so 4108 // same as "external to LB" above. 4109 output: "10.180.0.3:80, 10.180.1.3:80", 4110 masq: true, 4111 }, 4112 } 4113 4114 type packetFlowTestOverride struct { 4115 output *string 4116 masq *bool 4117 } 4118 4119 testCases := []struct { 4120 name string 4121 line string 4122 masqueradeAll bool 4123 localDetector bool 4124 overrides map[string]packetFlowTestOverride 4125 }{ 4126 { 4127 name: "base", 4128 line: getLine(), 4129 masqueradeAll: false, 4130 localDetector: true, 4131 overrides: nil, 4132 }, 4133 { 4134 name: "no LocalTrafficDetector", 4135 line: getLine(), 4136 masqueradeAll: false, 4137 localDetector: false, 4138 overrides: map[string]packetFlowTestOverride{ 4139 // With no LocalTrafficDetector, all traffic to a 4140 // ClusterIP is assumed to be from a pod, and thus to not 4141 // require masquerading. 4142 "node to ClusterIP": { 4143 masq: ptr.To(false), 4144 }, 4145 "node to ClusterIP with eTP:Local": { 4146 masq: ptr.To(false), 4147 }, 4148 "node to ClusterIP with iTP:Local": { 4149 masq: ptr.To(false), 4150 }, 4151 "external to ClusterIP": { 4152 masq: ptr.To(false), 4153 }, 4154 "external to ClusterIP with eTP:Local": { 4155 masq: ptr.To(false), 4156 }, 4157 "external to ClusterIP with iTP:Local": { 4158 masq: ptr.To(false), 4159 }, 4160 4161 // And there's no eTP:Local short-circuit for pod traffic, 4162 // so pods get only the local endpoints. 4163 "pod to NodePort with eTP:Local": { 4164 output: ptr.To("10.180.0.2:80"), 4165 }, 4166 "pod to LB with eTP:Local": { 4167 output: ptr.To("10.180.0.2:80"), 4168 }, 4169 }, 4170 }, 4171 { 4172 name: "masqueradeAll", 4173 line: getLine(), 4174 masqueradeAll: true, 4175 localDetector: true, 4176 overrides: map[string]packetFlowTestOverride{ 4177 // All "to ClusterIP" traffic gets masqueraded when using 4178 // --masquerade-all. 4179 "pod to ClusterIP": { 4180 masq: ptr.To(true), 4181 }, 4182 "pod to ClusterIP with eTP:Local": { 4183 masq: ptr.To(true), 4184 }, 4185 "pod to ClusterIP with iTP:Local": { 4186 masq: ptr.To(true), 4187 }, 4188 }, 4189 }, 4190 { 4191 name: "masqueradeAll, no LocalTrafficDetector", 4192 line: getLine(), 4193 masqueradeAll: true, 4194 localDetector: false, 4195 overrides: map[string]packetFlowTestOverride{ 4196 // As in "masqueradeAll" 4197 "pod to ClusterIP": { 4198 masq: ptr.To(true), 4199 }, 4200 "pod to ClusterIP with eTP:Local": { 4201 masq: ptr.To(true), 4202 }, 4203 "pod to ClusterIP with iTP:Local": { 4204 masq: ptr.To(true), 4205 }, 4206 4207 // As in "no LocalTrafficDetector" 4208 "pod to NodePort with eTP:Local": { 4209 output: ptr.To("10.180.0.2:80"), 4210 }, 4211 "pod to LB with eTP:Local": { 4212 output: ptr.To("10.180.0.2:80"), 4213 }, 4214 }, 4215 }, 4216 } 4217 4218 for _, tc := range testCases { 4219 t.Run(tc.name, func(t *testing.T) { 4220 nft, fp := NewFakeProxier(v1.IPv4Protocol) 4221 fp.masqueradeAll = tc.masqueradeAll 4222 if !tc.localDetector { 4223 fp.localDetector = proxyutiliptables.NewNoOpLocalDetector() 4224 } 4225 setupTest(fp) 4226 4227 // Merge base flowTests with per-test-case overrides 4228 tcFlowTests := make([]packetFlowTest, len(flowTests)) 4229 overridesApplied := 0 4230 for i := range flowTests { 4231 tcFlowTests[i] = flowTests[i] 4232 if overrides, set := tc.overrides[flowTests[i].name]; set { 4233 overridesApplied++ 4234 if overrides.masq != nil { 4235 if tcFlowTests[i].masq == *overrides.masq { 4236 t.Errorf("%q override value for masq is same as base value", flowTests[i].name) 4237 } 4238 tcFlowTests[i].masq = *overrides.masq 4239 } 4240 if overrides.output != nil { 4241 if tcFlowTests[i].output == *overrides.output { 4242 t.Errorf("%q override value for output is same as base value", flowTests[i].name) 4243 } 4244 tcFlowTests[i].output = *overrides.output 4245 } 4246 } 4247 } 4248 if overridesApplied != len(tc.overrides) { 4249 t.Errorf("%d overrides did not match any test case name!", len(tc.overrides)-overridesApplied) 4250 } 4251 runPacketFlowTests(t, tc.line, nft, testNodeIPs, tcFlowTests) 4252 }) 4253 } 4254 } 4255 4256 // Test calling syncProxyRules() multiple times with various changes 4257 func TestSyncProxyRulesRepeated(t *testing.T) { 4258 nft, fp := NewFakeProxier(v1.IPv4Protocol) 4259 4260 baseRules := dedent.Dedent(` 4261 add table ip kube-proxy { comment "rules for kube-proxy" ; } 4262 4263 add chain ip kube-proxy endpoints-check 4264 add chain ip kube-proxy filter-forward { type filter hook forward priority -101 ; } 4265 add chain ip kube-proxy filter-input { type filter hook input priority -101 ; } 4266 add chain ip kube-proxy filter-output { type filter hook output priority -101 ; } 4267 add chain ip kube-proxy firewall-allow-check 4268 add chain ip kube-proxy firewall-check 4269 add chain ip kube-proxy forward 4270 add chain ip kube-proxy mark-for-masquerade 4271 add chain ip kube-proxy masquerading 4272 add chain ip kube-proxy nat-output { type nat hook output priority -100 ; } 4273 add chain ip kube-proxy nat-postrouting { type nat hook postrouting priority 100 ; } 4274 add chain ip kube-proxy nat-prerouting { type nat hook prerouting priority -100 ; } 4275 add chain ip kube-proxy reject-chain { comment "helper for @no-endpoint-services / @no-endpoint-nodeports" ; } 4276 add chain ip kube-proxy services 4277 4278 add rule ip kube-proxy endpoints-check ip daddr . meta l4proto . th dport vmap @no-endpoint-services 4279 add rule ip kube-proxy endpoints-check fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @no-endpoint-nodeports 4280 add rule ip kube-proxy filter-forward ct state new jump endpoints-check 4281 add rule ip kube-proxy filter-forward jump forward 4282 add rule ip kube-proxy filter-forward ct state new jump firewall-check 4283 add rule ip kube-proxy filter-input ct state new jump endpoints-check 4284 add rule ip kube-proxy filter-input ct state new jump firewall-check 4285 add rule ip kube-proxy filter-output ct state new jump endpoints-check 4286 add rule ip kube-proxy filter-output ct state new jump firewall-check 4287 add rule ip kube-proxy firewall-allow-check ip daddr . meta l4proto . th dport . ip saddr @firewall-allow return 4288 add rule ip kube-proxy firewall-allow-check drop 4289 add rule ip kube-proxy firewall-check ip daddr . meta l4proto . th dport @firewall jump firewall-allow-check 4290 add rule ip kube-proxy forward ct state invalid drop 4291 add rule ip kube-proxy mark-for-masquerade mark set mark or 0x4000 4292 add rule ip kube-proxy masquerading mark and 0x4000 == 0 return 4293 add rule ip kube-proxy masquerading mark set mark xor 0x4000 4294 add rule ip kube-proxy masquerading masquerade fully-random 4295 add rule ip kube-proxy nat-output jump services 4296 add rule ip kube-proxy nat-postrouting jump masquerading 4297 add rule ip kube-proxy nat-prerouting jump services 4298 add rule ip kube-proxy reject-chain reject 4299 add rule ip kube-proxy services ip daddr . meta l4proto . th dport vmap @service-ips 4300 add rule ip kube-proxy services fib daddr type local ip daddr != 127.0.0.0/8 meta l4proto . th dport vmap @service-nodeports 4301 4302 add set ip kube-proxy firewall { type ipv4_addr . inet_proto . inet_service ; comment "destinations that are subject to LoadBalancerSourceRanges" ; } 4303 add set ip kube-proxy firewall-allow { type ipv4_addr . inet_proto . inet_service . ipv4_addr ; flags interval ; comment "destinations+sources that are allowed by LoadBalancerSourceRanges" ; } 4304 add map ip kube-proxy no-endpoint-nodeports { type inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to service nodeports with no endpoints" ; } 4305 add map ip kube-proxy no-endpoint-services { type ipv4_addr . inet_proto . inet_service : verdict ; comment "vmap to drop or reject packets to services with no endpoints" ; } 4306 add map ip kube-proxy service-ips { type ipv4_addr . inet_proto . inet_service : verdict ; comment "ClusterIP, ExternalIP and LoadBalancer IP traffic" ; } 4307 add map ip kube-proxy service-nodeports { type inet_proto . inet_service : verdict ; comment "NodePort traffic" ; } 4308 `) 4309 4310 // Helper function to make it look like time has passed (from the point of view of 4311 // the stale-chain-deletion code). 4312 ageStaleChains := func() { 4313 for chain, t := range fp.staleChains { 4314 fp.staleChains[chain] = t.Add(-2 * time.Second) 4315 } 4316 } 4317 4318 // Create initial state 4319 var svc2 *v1.Service 4320 4321 makeServiceMap(fp, 4322 makeTestService("ns1", "svc1", func(svc *v1.Service) { 4323 svc.Spec.Type = v1.ServiceTypeClusterIP 4324 svc.Spec.ClusterIP = "172.30.0.41" 4325 svc.Spec.Ports = []v1.ServicePort{{ 4326 Name: "p80", 4327 Port: 80, 4328 Protocol: v1.ProtocolTCP, 4329 }} 4330 }), 4331 makeTestService("ns2", "svc2", func(svc *v1.Service) { 4332 svc2 = svc 4333 svc.Spec.Type = v1.ServiceTypeClusterIP 4334 svc.Spec.ClusterIP = "172.30.0.42" 4335 svc.Spec.Ports = []v1.ServicePort{{ 4336 Name: "p8080", 4337 Port: 8080, 4338 Protocol: v1.ProtocolTCP, 4339 }} 4340 }), 4341 ) 4342 4343 populateEndpointSlices(fp, 4344 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 4345 eps.AddressType = discovery.AddressTypeIPv4 4346 eps.Endpoints = []discovery.Endpoint{{ 4347 Addresses: []string{"10.0.1.1"}, 4348 }} 4349 eps.Ports = []discovery.EndpointPort{{ 4350 Name: ptr.To("p80"), 4351 Port: ptr.To[int32](80), 4352 Protocol: ptr.To(v1.ProtocolTCP), 4353 }} 4354 }), 4355 makeTestEndpointSlice("ns2", "svc2", 1, func(eps *discovery.EndpointSlice) { 4356 eps.AddressType = discovery.AddressTypeIPv4 4357 eps.Endpoints = []discovery.Endpoint{{ 4358 Addresses: []string{"10.0.2.1"}, 4359 }} 4360 eps.Ports = []discovery.EndpointPort{{ 4361 Name: ptr.To("p8080"), 4362 Port: ptr.To[int32](8080), 4363 Protocol: ptr.To(v1.ProtocolTCP), 4364 }} 4365 }), 4366 ) 4367 4368 fp.syncProxyRules() 4369 4370 expected := baseRules + dedent.Dedent(` 4371 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4372 add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 8080 : goto service-MHHHYRWA-ns2/svc2/tcp/p8080 } 4373 4374 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4375 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4376 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4377 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4378 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4379 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4380 4381 add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 4382 add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 ip daddr 172.30.0.42 tcp dport 8080 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4383 add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 numgen random mod 1 vmap { 0 : goto endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 } 4384 add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 4385 add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 ip saddr 10.0.2.1 jump mark-for-masquerade 4386 add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 meta l4proto tcp dnat to 10.0.2.1:8080 4387 `) 4388 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4389 4390 // Add a new service and its endpoints 4391 makeServiceMap(fp, 4392 makeTestService("ns3", "svc3", func(svc *v1.Service) { 4393 svc.Spec.Type = v1.ServiceTypeClusterIP 4394 svc.Spec.ClusterIP = "172.30.0.43" 4395 svc.Spec.Ports = []v1.ServicePort{{ 4396 Name: "p80", 4397 Port: 80, 4398 Protocol: v1.ProtocolTCP, 4399 }} 4400 }), 4401 ) 4402 var eps3 *discovery.EndpointSlice 4403 populateEndpointSlices(fp, 4404 makeTestEndpointSlice("ns3", "svc3", 1, func(eps *discovery.EndpointSlice) { 4405 eps3 = eps 4406 eps.AddressType = discovery.AddressTypeIPv4 4407 eps.Endpoints = []discovery.Endpoint{{ 4408 Addresses: []string{"10.0.3.1"}, 4409 }} 4410 eps.Ports = []discovery.EndpointPort{{ 4411 Name: ptr.To("p80"), 4412 Port: ptr.To[int32](80), 4413 Protocol: ptr.To(v1.ProtocolTCP), 4414 }} 4415 }), 4416 ) 4417 fp.syncProxyRules() 4418 4419 expected = baseRules + dedent.Dedent(` 4420 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4421 add element ip kube-proxy service-ips { 172.30.0.42 . tcp . 8080 : goto service-MHHHYRWA-ns2/svc2/tcp/p8080 } 4422 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4423 4424 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4425 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4426 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4427 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4428 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4429 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4430 4431 add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 4432 add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 ip daddr 172.30.0.42 tcp dport 8080 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4433 add rule ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 numgen random mod 1 vmap { 0 : goto endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 } 4434 add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 4435 add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 ip saddr 10.0.2.1 jump mark-for-masquerade 4436 add rule ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 meta l4proto tcp dnat to 10.0.2.1:8080 4437 4438 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4439 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4440 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 } 4441 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4442 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade 4443 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80 4444 `) 4445 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4446 4447 // Delete a service; its chains will be flushed, but not immediately deleted. 4448 fp.OnServiceDelete(svc2) 4449 fp.syncProxyRules() 4450 expected = baseRules + dedent.Dedent(` 4451 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4452 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4453 4454 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4455 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4456 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4457 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4458 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4459 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4460 4461 add chain ip kube-proxy service-MHHHYRWA-ns2/svc2/tcp/p8080 4462 add chain ip kube-proxy endpoint-7RVP4LUQ-ns2/svc2/tcp/p8080__10.0.2.1/8080 4463 4464 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4465 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4466 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 } 4467 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4468 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade 4469 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80 4470 `) 4471 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4472 4473 // Fake the passage of time and confirm that the stale chains get deleted. 4474 ageStaleChains() 4475 fp.syncProxyRules() 4476 expected = baseRules + dedent.Dedent(` 4477 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4478 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4479 4480 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4481 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4482 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4483 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4484 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4485 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4486 4487 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4488 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4489 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 } 4490 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4491 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade 4492 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80 4493 `) 4494 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4495 4496 // Add a service, sync, then add its endpoints. 4497 makeServiceMap(fp, 4498 makeTestService("ns4", "svc4", func(svc *v1.Service) { 4499 svc.Spec.Type = v1.ServiceTypeClusterIP 4500 svc.Spec.ClusterIP = "172.30.0.44" 4501 svc.Spec.Ports = []v1.ServicePort{{ 4502 Name: "p80", 4503 Port: 80, 4504 Protocol: v1.ProtocolTCP, 4505 }} 4506 }), 4507 ) 4508 fp.syncProxyRules() 4509 expected = baseRules + dedent.Dedent(` 4510 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4511 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4512 4513 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4514 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4515 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4516 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4517 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4518 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4519 4520 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4521 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4522 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 } 4523 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4524 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade 4525 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80 4526 4527 add element ip kube-proxy no-endpoint-services { 172.30.0.44 . tcp . 80 comment "ns4/svc4:p80" : goto reject-chain } 4528 `) 4529 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4530 4531 populateEndpointSlices(fp, 4532 makeTestEndpointSlice("ns4", "svc4", 1, func(eps *discovery.EndpointSlice) { 4533 eps.AddressType = discovery.AddressTypeIPv4 4534 eps.Endpoints = []discovery.Endpoint{{ 4535 Addresses: []string{"10.0.4.1"}, 4536 }} 4537 eps.Ports = []discovery.EndpointPort{{ 4538 Name: ptr.To("p80"), 4539 Port: ptr.To[int32](80), 4540 Protocol: ptr.To(v1.ProtocolTCP), 4541 }} 4542 }), 4543 ) 4544 fp.syncProxyRules() 4545 expected = baseRules + dedent.Dedent(` 4546 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4547 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4548 add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 } 4549 4550 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4551 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4552 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4553 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4554 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4555 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4556 4557 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4558 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4559 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 } 4560 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4561 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 ip saddr 10.0.3.1 jump mark-for-masquerade 4562 add rule ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 meta l4proto tcp dnat to 10.0.3.1:80 4563 4564 add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 4565 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4566 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 } 4567 add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 4568 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade 4569 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80 4570 `) 4571 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4572 4573 // Change an endpoint of an existing service. 4574 eps3update := eps3.DeepCopy() 4575 eps3update.Endpoints[0].Addresses[0] = "10.0.3.2" 4576 fp.OnEndpointSliceUpdate(eps3, eps3update) 4577 fp.syncProxyRules() 4578 4579 // The old endpoint chain (for 10.0.3.1) will not be deleted yet. 4580 expected = baseRules + dedent.Dedent(` 4581 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4582 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4583 add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 } 4584 4585 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4586 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4587 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4588 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4589 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4590 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4591 4592 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4593 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4594 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 } 4595 add chain ip kube-proxy endpoint-2OCDJSZQ-ns3/svc3/tcp/p80__10.0.3.1/80 4596 add chain ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 4597 add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 ip saddr 10.0.3.2 jump mark-for-masquerade 4598 add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 meta l4proto tcp dnat to 10.0.3.2:80 4599 4600 add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 4601 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4602 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 } 4603 add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 4604 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade 4605 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80 4606 `) 4607 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4608 4609 // (Ensure the old svc3 chain gets deleted in the next sync.) 4610 ageStaleChains() 4611 4612 // Add an endpoint to a service. 4613 eps3update2 := eps3update.DeepCopy() 4614 eps3update2.Endpoints = append(eps3update2.Endpoints, discovery.Endpoint{Addresses: []string{"10.0.3.3"}}) 4615 fp.OnEndpointSliceUpdate(eps3update, eps3update2) 4616 fp.syncProxyRules() 4617 4618 expected = baseRules + dedent.Dedent(` 4619 add element ip kube-proxy service-ips { 172.30.0.41 . tcp . 80 : goto service-ULMVA6XW-ns1/svc1/tcp/p80 } 4620 add element ip kube-proxy service-ips { 172.30.0.43 . tcp . 80 : goto service-4AT6LBPK-ns3/svc3/tcp/p80 } 4621 add element ip kube-proxy service-ips { 172.30.0.44 . tcp . 80 : goto service-LAUZTJTB-ns4/svc4/tcp/p80 } 4622 4623 add chain ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 4624 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 ip daddr 172.30.0.41 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4625 add rule ip kube-proxy service-ULMVA6XW-ns1/svc1/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 } 4626 add chain ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 4627 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 ip saddr 10.0.1.1 jump mark-for-masquerade 4628 add rule ip kube-proxy endpoint-5TPGNJF2-ns1/svc1/tcp/p80__10.0.1.1/80 meta l4proto tcp dnat to 10.0.1.1:80 4629 4630 add chain ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 4631 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 ip daddr 172.30.0.43 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4632 add rule ip kube-proxy service-4AT6LBPK-ns3/svc3/tcp/p80 numgen random mod 2 vmap { 0 : goto endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 , 1 : goto endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 } 4633 add chain ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 4634 add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 ip saddr 10.0.3.2 jump mark-for-masquerade 4635 add rule ip kube-proxy endpoint-SWWHDC7X-ns3/svc3/tcp/p80__10.0.3.2/80 meta l4proto tcp dnat to 10.0.3.2:80 4636 add chain ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 4637 add rule ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 ip saddr 10.0.3.3 jump mark-for-masquerade 4638 add rule ip kube-proxy endpoint-TQ2QKHCZ-ns3/svc3/tcp/p80__10.0.3.3/80 meta l4proto tcp dnat to 10.0.3.3:80 4639 4640 add chain ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 4641 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 ip daddr 172.30.0.44 tcp dport 80 ip saddr != 10.0.0.0/8 jump mark-for-masquerade 4642 add rule ip kube-proxy service-LAUZTJTB-ns4/svc4/tcp/p80 numgen random mod 1 vmap { 0 : goto endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 } 4643 add chain ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 4644 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 ip saddr 10.0.4.1 jump mark-for-masquerade 4645 add rule ip kube-proxy endpoint-WAHRBT2B-ns4/svc4/tcp/p80__10.0.4.1/80 meta l4proto tcp dnat to 10.0.4.1:80 4646 `) 4647 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4648 4649 // Sync with no new changes, so same expected rules as last time 4650 fp.syncProxyRules() 4651 assertNFTablesTransactionEqual(t, getLine(), expected, nft.Dump()) 4652 } 4653 4654 func TestNoEndpointsMetric(t *testing.T) { 4655 type endpoint struct { 4656 ip string 4657 hostname string 4658 } 4659 4660 metrics.RegisterMetrics() 4661 testCases := []struct { 4662 name string 4663 internalTrafficPolicy *v1.ServiceInternalTrafficPolicy 4664 externalTrafficPolicy v1.ServiceExternalTrafficPolicy 4665 endpoints []endpoint 4666 expectedSyncProxyRulesNoLocalEndpointsTotalInternal int 4667 expectedSyncProxyRulesNoLocalEndpointsTotalExternal int 4668 }{ 4669 { 4670 name: "internalTrafficPolicy is set and there are local endpoints", 4671 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 4672 endpoints: []endpoint{ 4673 {"10.0.1.1", testHostname}, 4674 {"10.0.1.2", "host1"}, 4675 {"10.0.1.3", "host2"}, 4676 }, 4677 }, 4678 { 4679 name: "externalTrafficPolicy is set and there are local endpoints", 4680 externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 4681 endpoints: []endpoint{ 4682 {"10.0.1.1", testHostname}, 4683 {"10.0.1.2", "host1"}, 4684 {"10.0.1.3", "host2"}, 4685 }, 4686 }, 4687 { 4688 name: "both policies are set and there are local endpoints", 4689 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 4690 externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 4691 endpoints: []endpoint{ 4692 {"10.0.1.1", testHostname}, 4693 {"10.0.1.2", "host1"}, 4694 {"10.0.1.3", "host2"}, 4695 }, 4696 }, 4697 { 4698 name: "internalTrafficPolicy is set and there are no local endpoints", 4699 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 4700 endpoints: []endpoint{ 4701 {"10.0.1.1", "host0"}, 4702 {"10.0.1.2", "host1"}, 4703 {"10.0.1.3", "host2"}, 4704 }, 4705 expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 1, 4706 }, 4707 { 4708 name: "externalTrafficPolicy is set and there are no local endpoints", 4709 externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 4710 endpoints: []endpoint{ 4711 {"10.0.1.1", "host0"}, 4712 {"10.0.1.2", "host1"}, 4713 {"10.0.1.3", "host2"}, 4714 }, 4715 expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 1, 4716 }, 4717 { 4718 name: "both policies are set and there are no local endpoints", 4719 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 4720 externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 4721 endpoints: []endpoint{ 4722 {"10.0.1.1", "host0"}, 4723 {"10.0.1.2", "host1"}, 4724 {"10.0.1.3", "host2"}, 4725 }, 4726 expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 1, 4727 expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 1, 4728 }, 4729 { 4730 name: "both policies are set and there are no endpoints at all", 4731 internalTrafficPolicy: ptr.To(v1.ServiceInternalTrafficPolicyLocal), 4732 externalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, 4733 endpoints: []endpoint{}, 4734 expectedSyncProxyRulesNoLocalEndpointsTotalInternal: 0, 4735 expectedSyncProxyRulesNoLocalEndpointsTotalExternal: 0, 4736 }, 4737 } 4738 4739 for _, tc := range testCases { 4740 t.Run(tc.name, func(t *testing.T) { 4741 _, fp := NewFakeProxier(v1.IPv4Protocol) 4742 fp.OnServiceSynced() 4743 fp.OnEndpointSlicesSynced() 4744 4745 serviceName := "svc1" 4746 namespaceName := "ns1" 4747 4748 svc := &v1.Service{ 4749 ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespaceName}, 4750 Spec: v1.ServiceSpec{ 4751 ClusterIP: "172.30.1.1", 4752 Selector: map[string]string{"foo": "bar"}, 4753 Ports: []v1.ServicePort{{Name: "", Port: 80, Protocol: v1.ProtocolTCP, NodePort: 123}}, 4754 }, 4755 } 4756 if tc.internalTrafficPolicy != nil { 4757 svc.Spec.InternalTrafficPolicy = tc.internalTrafficPolicy 4758 } 4759 if tc.externalTrafficPolicy != "" { 4760 svc.Spec.Type = v1.ServiceTypeNodePort 4761 svc.Spec.ExternalTrafficPolicy = tc.externalTrafficPolicy 4762 } 4763 4764 fp.OnServiceAdd(svc) 4765 4766 endpointSlice := &discovery.EndpointSlice{ 4767 ObjectMeta: metav1.ObjectMeta{ 4768 Name: fmt.Sprintf("%s-1", serviceName), 4769 Namespace: namespaceName, 4770 Labels: map[string]string{discovery.LabelServiceName: serviceName}, 4771 }, 4772 Ports: []discovery.EndpointPort{{ 4773 Name: ptr.To(""), 4774 Port: ptr.To[int32](80), 4775 Protocol: ptr.To(v1.ProtocolTCP), 4776 }}, 4777 AddressType: discovery.AddressTypeIPv4, 4778 } 4779 for _, ep := range tc.endpoints { 4780 endpointSlice.Endpoints = append(endpointSlice.Endpoints, discovery.Endpoint{ 4781 Addresses: []string{ep.ip}, 4782 Conditions: discovery.EndpointConditions{Ready: ptr.To(true)}, 4783 NodeName: ptr.To(ep.hostname), 4784 }) 4785 } 4786 4787 fp.OnEndpointSliceAdd(endpointSlice) 4788 fp.syncProxyRules() 4789 syncProxyRulesNoLocalEndpointsTotalInternal, err := testutil.GetGaugeMetricValue(metrics.SyncProxyRulesNoLocalEndpointsTotal.WithLabelValues("internal")) 4790 if err != nil { 4791 t.Errorf("failed to get %s value, err: %v", metrics.SyncProxyRulesNoLocalEndpointsTotal.Name, err) 4792 } 4793 4794 if tc.expectedSyncProxyRulesNoLocalEndpointsTotalInternal != int(syncProxyRulesNoLocalEndpointsTotalInternal) { 4795 t.Errorf("sync_proxy_rules_no_endpoints_total metric mismatch(internal): got=%d, expected %d", int(syncProxyRulesNoLocalEndpointsTotalInternal), tc.expectedSyncProxyRulesNoLocalEndpointsTotalInternal) 4796 } 4797 4798 syncProxyRulesNoLocalEndpointsTotalExternal, err := testutil.GetGaugeMetricValue(metrics.SyncProxyRulesNoLocalEndpointsTotal.WithLabelValues("external")) 4799 if err != nil { 4800 t.Errorf("failed to get %s value(external), err: %v", metrics.SyncProxyRulesNoLocalEndpointsTotal.Name, err) 4801 } 4802 4803 if tc.expectedSyncProxyRulesNoLocalEndpointsTotalExternal != int(syncProxyRulesNoLocalEndpointsTotalExternal) { 4804 t.Errorf("sync_proxy_rules_no_endpoints_total metric mismatch(internal): got=%d, expected %d", int(syncProxyRulesNoLocalEndpointsTotalExternal), tc.expectedSyncProxyRulesNoLocalEndpointsTotalExternal) 4805 } 4806 }) 4807 } 4808 } 4809 4810 func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) { 4811 testCases := []struct { 4812 name string 4813 ipModeEnabled bool 4814 svcIP string 4815 svcLBIP string 4816 ipMode *v1.LoadBalancerIPMode 4817 expectedRule bool 4818 }{ 4819 /* LoadBalancerIPMode disabled */ 4820 { 4821 name: "LoadBalancerIPMode disabled, ipMode Proxy", 4822 ipModeEnabled: false, 4823 svcIP: "10.20.30.41", 4824 svcLBIP: "1.2.3.4", 4825 ipMode: ptr.To(v1.LoadBalancerIPModeProxy), 4826 expectedRule: true, 4827 }, 4828 { 4829 name: "LoadBalancerIPMode disabled, ipMode VIP", 4830 ipModeEnabled: false, 4831 svcIP: "10.20.30.42", 4832 svcLBIP: "1.2.3.5", 4833 ipMode: ptr.To(v1.LoadBalancerIPModeVIP), 4834 expectedRule: true, 4835 }, 4836 { 4837 name: "LoadBalancerIPMode disabled, ipMode nil", 4838 ipModeEnabled: false, 4839 svcIP: "10.20.30.43", 4840 svcLBIP: "1.2.3.6", 4841 ipMode: nil, 4842 expectedRule: true, 4843 }, 4844 /* LoadBalancerIPMode enabled */ 4845 { 4846 name: "LoadBalancerIPMode enabled, ipMode Proxy", 4847 ipModeEnabled: true, 4848 svcIP: "10.20.30.41", 4849 svcLBIP: "1.2.3.4", 4850 ipMode: ptr.To(v1.LoadBalancerIPModeProxy), 4851 expectedRule: false, 4852 }, 4853 { 4854 name: "LoadBalancerIPMode enabled, ipMode VIP", 4855 ipModeEnabled: true, 4856 svcIP: "10.20.30.42", 4857 svcLBIP: "1.2.3.5", 4858 ipMode: ptr.To(v1.LoadBalancerIPModeVIP), 4859 expectedRule: true, 4860 }, 4861 { 4862 name: "LoadBalancerIPMode enabled, ipMode nil", 4863 ipModeEnabled: true, 4864 svcIP: "10.20.30.43", 4865 svcLBIP: "1.2.3.6", 4866 ipMode: nil, 4867 expectedRule: true, 4868 }, 4869 } 4870 4871 svcPort := 80 4872 svcNodePort := 3001 4873 svcPortName := proxy.ServicePortName{ 4874 NamespacedName: makeNSN("ns1", "svc1"), 4875 Port: "p80", 4876 Protocol: v1.ProtocolTCP, 4877 } 4878 4879 for _, testCase := range testCases { 4880 t.Run(testCase.name, func(t *testing.T) { 4881 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, testCase.ipModeEnabled)() 4882 nft, fp := NewFakeProxier(v1.IPv4Protocol) 4883 makeServiceMap(fp, 4884 makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { 4885 svc.Spec.Type = "LoadBalancer" 4886 svc.Spec.ClusterIP = testCase.svcIP 4887 svc.Spec.Ports = []v1.ServicePort{{ 4888 Name: svcPortName.Port, 4889 Port: int32(svcPort), 4890 Protocol: v1.ProtocolTCP, 4891 NodePort: int32(svcNodePort), 4892 }} 4893 svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{ 4894 IP: testCase.svcLBIP, 4895 IPMode: testCase.ipMode, 4896 }} 4897 }), 4898 ) 4899 4900 populateEndpointSlices(fp, 4901 makeTestEndpointSlice("ns1", "svc1", 1, func(eps *discovery.EndpointSlice) { 4902 eps.AddressType = discovery.AddressTypeIPv4 4903 eps.Endpoints = []discovery.Endpoint{{ 4904 Addresses: []string{"10.180.0.1"}, 4905 }} 4906 eps.Ports = []discovery.EndpointPort{{ 4907 Name: ptr.To("p80"), 4908 Port: ptr.To[int32](80), 4909 Protocol: ptr.To(v1.ProtocolTCP), 4910 }} 4911 }), 4912 ) 4913 4914 fp.syncProxyRules() 4915 4916 element := nft.Table.Maps["service-ips"].FindElement(testCase.svcLBIP, "tcp", "80") 4917 ruleExists := element != nil 4918 if ruleExists != testCase.expectedRule { 4919 t.Errorf("unexpected rule for %s", testCase.svcLBIP) 4920 } 4921 }) 4922 } 4923 } 4924 4925 func Test_servicePortChainNameBase(t *testing.T) { 4926 testCases := []struct { 4927 name string 4928 spn proxy.ServicePortName 4929 protocol string 4930 expected string 4931 }{ 4932 { 4933 name: "simple", 4934 spn: proxy.ServicePortName{ 4935 NamespacedName: types.NamespacedName{ 4936 Namespace: "testing", 4937 Name: "service", 4938 }, 4939 Port: "http", 4940 }, 4941 protocol: "tcp", 4942 expected: "P4ZYZVCF-testing/service/tcp/http", 4943 }, 4944 { 4945 name: "different port, different hash", 4946 spn: proxy.ServicePortName{ 4947 NamespacedName: types.NamespacedName{ 4948 Namespace: "testing", 4949 Name: "service", 4950 }, 4951 Port: "https", 4952 }, 4953 protocol: "tcp", 4954 expected: "LZBRENCP-testing/service/tcp/https", 4955 }, 4956 { 4957 name: "max length", 4958 spn: proxy.ServicePortName{ 4959 NamespacedName: types.NamespacedName{ 4960 Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx", 4961 Name: "very-long-service-name-why-would-you-even-do-this-i-mean-really", 4962 }, 4963 Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls", 4964 }, 4965 protocol: "sctp", 4966 expected: "KR6NACJP-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls", 4967 }, 4968 } 4969 4970 for _, tc := range testCases { 4971 t.Run(tc.name, func(t *testing.T) { 4972 name := servicePortChainNameBase(&tc.spn, tc.protocol) 4973 if name != tc.expected { 4974 t.Errorf("expected %q, got %q", tc.expected, name) 4975 } 4976 }) 4977 } 4978 } 4979 4980 func Test_servicePortEndpointChainNameBase(t *testing.T) { 4981 testCases := []struct { 4982 name string 4983 spn proxy.ServicePortName 4984 protocol string 4985 endpoint string 4986 expected string 4987 }{ 4988 { 4989 name: "simple", 4990 spn: proxy.ServicePortName{ 4991 NamespacedName: types.NamespacedName{ 4992 Namespace: "testing", 4993 Name: "service", 4994 }, 4995 Port: "http", 4996 }, 4997 protocol: "tcp", 4998 endpoint: "10.180.0.1:80", 4999 expected: "JO2XBXZR-testing/service/tcp/http__10.180.0.1/80", 5000 }, 5001 { 5002 name: "different endpoint, different hash", 5003 spn: proxy.ServicePortName{ 5004 NamespacedName: types.NamespacedName{ 5005 Namespace: "testing", 5006 Name: "service", 5007 }, 5008 Port: "http", 5009 }, 5010 protocol: "tcp", 5011 endpoint: "10.180.0.2:80", 5012 expected: "5S6H3H22-testing/service/tcp/http__10.180.0.2/80", 5013 }, 5014 { 5015 name: "ipv6", 5016 spn: proxy.ServicePortName{ 5017 NamespacedName: types.NamespacedName{ 5018 Namespace: "testing", 5019 Name: "service", 5020 }, 5021 Port: "http", 5022 }, 5023 protocol: "tcp", 5024 endpoint: "[fd80:abcd:12::a1b2:c3d4:e5f6:9999]:80", 5025 expected: "U7E2ET36-testing/service/tcp/http__fd80.abcd.12..a1b2.c3d4.e5f6.9999/80", 5026 }, 5027 { 5028 name: "max length without truncation", 5029 spn: proxy.ServicePortName{ 5030 NamespacedName: types.NamespacedName{ 5031 Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx", 5032 Name: "very-long-service-name-why-would-you-even-do-this-i-mean-really", 5033 }, 5034 Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls", 5035 }, 5036 protocol: "sctp", 5037 endpoint: "[1234:5678:9abc:def0::abc:1234]:443", 5038 expected: "5YS7AFEA-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abc.1234/443", 5039 }, 5040 { 5041 name: "truncated, 1", 5042 spn: proxy.ServicePortName{ 5043 NamespacedName: types.NamespacedName{ 5044 Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx", 5045 Name: "very-long-service-name-why-would-you-even-do-this-i-mean-really", 5046 }, 5047 Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls", 5048 }, 5049 protocol: "sctp", 5050 endpoint: "[1234:5678:9abc:def0::abcd:1234:5678]:443", 5051 expected: "CI6C53Q3-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abcd.1234...", 5052 }, 5053 { 5054 name: "truncated, 2 (different IP, which is not visible in the result)", 5055 spn: proxy.ServicePortName{ 5056 NamespacedName: types.NamespacedName{ 5057 Namespace: "very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx", 5058 Name: "very-long-service-name-why-would-you-even-do-this-i-mean-really", 5059 }, 5060 Port: "port-443-providing-the-hypertext-transmission-protocol-with-tls", 5061 }, 5062 protocol: "sctp", 5063 endpoint: "[1234:5678:9abc:def0::abcd:1234:8765]:443", 5064 expected: "2FLXFK6X-very-long-namespace-name-abcdefghijklmnopqrstuvwxyz0123456789xx/very-long-service-name-why-would-you-even-do-this-i-mean-really/sctp/port-443-providing-the-hypertext-transmission-protocol-with-tls__1234.5678.9abc.def0..abcd.1234...", 5065 }, 5066 } 5067 5068 for _, tc := range testCases { 5069 t.Run(tc.name, func(t *testing.T) { 5070 name := servicePortEndpointChainNameBase(&tc.spn, tc.protocol, tc.endpoint) 5071 if name != tc.expected { 5072 t.Errorf("expected %q, got %q", tc.expected, name) 5073 } 5074 }) 5075 } 5076 }