k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/proxy/conntrack/cleanup_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2023 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package conntrack 21 22 import ( 23 "net" 24 "reflect" 25 "testing" 26 27 v1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/types" 30 "k8s.io/apimachinery/pkg/util/sets" 31 "k8s.io/kubernetes/pkg/proxy" 32 ) 33 34 const ( 35 testClusterIP = "172.30.1.1" 36 testExternalIP = "192.168.99.100" 37 testLoadBalancerIP = "1.2.3.4" 38 39 testEndpointIP = "10.240.0.4" 40 41 testPort = 53 42 testNodePort = 5353 43 testEndpointPort = "5300" 44 ) 45 46 func TestCleanStaleEntries(t *testing.T) { 47 // We need to construct a proxy.ServicePortMap to pass to CleanStaleEntries. 48 // ServicePortMap is just map[string]proxy.ServicePort, but there are no public 49 // constructors for any implementation of proxy.ServicePort, so we have to either 50 // provide our own implementation of that interface, or else use a 51 // proxy.ServiceChangeTracker to construct them and fill in the map for us. 52 53 sct := proxy.NewServiceChangeTracker(nil, v1.IPv4Protocol, nil, nil) 54 svc := &v1.Service{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Name: "cleanup-test", 57 Namespace: "test", 58 }, 59 Spec: v1.ServiceSpec{ 60 ClusterIP: testClusterIP, 61 ExternalIPs: []string{testExternalIP}, 62 Ports: []v1.ServicePort{ 63 { 64 Name: "dns-tcp", 65 Port: testPort, 66 Protocol: v1.ProtocolTCP, 67 }, 68 { 69 Name: "dns-udp", 70 Port: testPort, 71 NodePort: testNodePort, 72 Protocol: v1.ProtocolUDP, 73 }, 74 }, 75 }, 76 Status: v1.ServiceStatus{ 77 LoadBalancer: v1.LoadBalancerStatus{ 78 Ingress: []v1.LoadBalancerIngress{{ 79 IP: testLoadBalancerIP, 80 }}, 81 }, 82 }, 83 } 84 sct.Update(nil, svc) 85 86 svcPortMap := make(proxy.ServicePortMap) 87 _ = svcPortMap.Update(sct) 88 89 // (At this point we are done with sct, and in particular, we don't use sct to 90 // construct UpdateServiceMapResults, because pkg/proxy already has its own tests 91 // for that. Also, svcPortMap is read-only from this point on.) 92 93 tcpPortName := proxy.ServicePortName{ 94 NamespacedName: types.NamespacedName{ 95 Namespace: svc.Namespace, 96 Name: svc.Name, 97 }, 98 Port: svc.Spec.Ports[0].Name, 99 Protocol: svc.Spec.Ports[0].Protocol, 100 } 101 102 udpPortName := proxy.ServicePortName{ 103 NamespacedName: types.NamespacedName{ 104 Namespace: svc.Namespace, 105 Name: svc.Name, 106 }, 107 Port: svc.Spec.Ports[1].Name, 108 Protocol: svc.Spec.Ports[1].Protocol, 109 } 110 111 unknownPortName := udpPortName 112 unknownPortName.Namespace = "unknown" 113 114 // Sanity-check to make sure we constructed the map correctly 115 if len(svcPortMap) != 2 { 116 t.Fatalf("expected svcPortMap to have 2 entries, got %+v", svcPortMap) 117 } 118 servicePort := svcPortMap[tcpPortName] 119 if servicePort == nil || servicePort.String() != "172.30.1.1:53/TCP" { 120 t.Fatalf("expected svcPortMap[%q] to be \"172.30.1.1:53/TCP\", got %q", tcpPortName.String(), servicePort.String()) 121 } 122 servicePort = svcPortMap[udpPortName] 123 if servicePort == nil || servicePort.String() != "172.30.1.1:53/UDP" { 124 t.Fatalf("expected svcPortMap[%q] to be \"172.30.1.1:53/UDP\", got %q", udpPortName.String(), servicePort.String()) 125 } 126 127 testCases := []struct { 128 description string 129 130 serviceUpdates proxy.UpdateServiceMapResult 131 endpointsUpdates proxy.UpdateEndpointsMapResult 132 133 result FakeInterface 134 }{ 135 { 136 description: "DeletedUDPClusterIPs clears entries for given clusterIPs (only)", 137 138 serviceUpdates: proxy.UpdateServiceMapResult{ 139 // Note: this isn't testClusterIP; it's the IP of some 140 // unknown (because deleted) service. 141 DeletedUDPClusterIPs: sets.New("172.30.99.99"), 142 }, 143 endpointsUpdates: proxy.UpdateEndpointsMapResult{}, 144 145 result: FakeInterface{ 146 ClearedIPs: sets.New("172.30.99.99"), 147 148 ClearedPorts: sets.New[int](), 149 ClearedNATs: map[string]string{}, 150 ClearedPortNATs: map[int]string{}, 151 }, 152 }, 153 { 154 description: "DeletedUDPEndpoints clears NAT entries for all IPs and NodePorts", 155 156 serviceUpdates: proxy.UpdateServiceMapResult{ 157 DeletedUDPClusterIPs: sets.New[string](), 158 }, 159 endpointsUpdates: proxy.UpdateEndpointsMapResult{ 160 DeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 161 Endpoint: net.JoinHostPort(testEndpointIP, testEndpointPort), 162 ServicePortName: udpPortName, 163 }}, 164 }, 165 166 result: FakeInterface{ 167 ClearedIPs: sets.New[string](), 168 ClearedPorts: sets.New[int](), 169 170 ClearedNATs: map[string]string{ 171 testClusterIP: testEndpointIP, 172 testExternalIP: testEndpointIP, 173 testLoadBalancerIP: testEndpointIP, 174 }, 175 ClearedPortNATs: map[int]string{ 176 testNodePort: testEndpointIP, 177 }, 178 }, 179 }, 180 { 181 description: "NewlyActiveUDPServices clears entries for all IPs and NodePorts", 182 183 serviceUpdates: proxy.UpdateServiceMapResult{ 184 DeletedUDPClusterIPs: sets.New[string](), 185 }, 186 endpointsUpdates: proxy.UpdateEndpointsMapResult{ 187 DeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 188 NewlyActiveUDPServices: []proxy.ServicePortName{ 189 udpPortName, 190 }, 191 }, 192 193 result: FakeInterface{ 194 ClearedIPs: sets.New(testClusterIP, testExternalIP, testLoadBalancerIP), 195 ClearedPorts: sets.New(testNodePort), 196 197 ClearedNATs: map[string]string{}, 198 ClearedPortNATs: map[int]string{}, 199 }, 200 }, 201 202 { 203 description: "DeletedUDPEndpoints for unknown Service has no effect", 204 205 serviceUpdates: proxy.UpdateServiceMapResult{ 206 DeletedUDPClusterIPs: sets.New[string](), 207 }, 208 endpointsUpdates: proxy.UpdateEndpointsMapResult{ 209 DeletedUDPEndpoints: []proxy.ServiceEndpoint{{ 210 Endpoint: "10.240.0.4:80", 211 ServicePortName: unknownPortName, 212 }}, 213 NewlyActiveUDPServices: []proxy.ServicePortName{}, 214 }, 215 216 result: FakeInterface{ 217 ClearedIPs: sets.New[string](), 218 ClearedPorts: sets.New[int](), 219 ClearedNATs: map[string]string{}, 220 ClearedPortNATs: map[int]string{}, 221 }, 222 }, 223 { 224 description: "NewlyActiveUDPServices for unknown Service has no effect", 225 226 serviceUpdates: proxy.UpdateServiceMapResult{ 227 DeletedUDPClusterIPs: sets.New[string](), 228 }, 229 endpointsUpdates: proxy.UpdateEndpointsMapResult{ 230 DeletedUDPEndpoints: []proxy.ServiceEndpoint{}, 231 NewlyActiveUDPServices: []proxy.ServicePortName{ 232 unknownPortName, 233 }, 234 }, 235 236 result: FakeInterface{ 237 ClearedIPs: sets.New[string](), 238 ClearedPorts: sets.New[int](), 239 ClearedNATs: map[string]string{}, 240 ClearedPortNATs: map[int]string{}, 241 }, 242 }, 243 } 244 245 for _, tc := range testCases { 246 t.Run(tc.description, func(t *testing.T) { 247 fake := NewFake() 248 CleanStaleEntries(fake, svcPortMap, tc.serviceUpdates, tc.endpointsUpdates) 249 if !fake.ClearedIPs.Equal(tc.result.ClearedIPs) { 250 t.Errorf("Expected ClearedIPs=%v, got %v", tc.result.ClearedIPs, fake.ClearedIPs) 251 } 252 if !fake.ClearedPorts.Equal(tc.result.ClearedPorts) { 253 t.Errorf("Expected ClearedPorts=%v, got %v", tc.result.ClearedPorts, fake.ClearedPorts) 254 } 255 if !reflect.DeepEqual(fake.ClearedNATs, tc.result.ClearedNATs) { 256 t.Errorf("Expected ClearedNATs=%v, got %v", tc.result.ClearedNATs, fake.ClearedNATs) 257 } 258 if !reflect.DeepEqual(fake.ClearedPortNATs, tc.result.ClearedPortNATs) { 259 t.Errorf("Expected ClearedPortNATs=%v, got %v", tc.result.ClearedPortNATs, fake.ClearedPortNATs) 260 } 261 }) 262 } 263 }