k8s.io/apiserver@v0.31.1/pkg/util/proxy/proxy_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package proxy 18 19 import ( 20 "net/url" 21 "testing" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/util/intstr" 26 v1listers "k8s.io/client-go/listers/core/v1" 27 "k8s.io/client-go/tools/cache" 28 ) 29 30 func TestResolve(t *testing.T) { 31 matchingEndpoints := func(svc *v1.Service) []*v1.Endpoints { 32 ports := []v1.EndpointPort{} 33 for _, p := range svc.Spec.Ports { 34 if p.TargetPort.Type != intstr.Int { 35 continue 36 } 37 ports = append(ports, v1.EndpointPort{Name: p.Name, Port: p.TargetPort.IntVal}) 38 } 39 40 return []*v1.Endpoints{{ 41 ObjectMeta: metav1.ObjectMeta{Namespace: svc.Namespace, Name: svc.Name}, 42 Subsets: []v1.EndpointSubset{{ 43 Addresses: []v1.EndpointAddress{{Hostname: "dummy-host", IP: "127.0.0.1"}}, 44 Ports: ports, 45 }}, 46 }} 47 } 48 49 type expectation struct { 50 url string 51 error bool 52 } 53 54 tests := []struct { 55 name string 56 services []*v1.Service 57 endpoints func(svc *v1.Service) []*v1.Endpoints 58 59 clusterMode expectation 60 endpointMode expectation 61 }{ 62 { 63 name: "cluster ip without 443 port", 64 services: []*v1.Service{ 65 { 66 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 67 Spec: v1.ServiceSpec{ 68 Type: v1.ServiceTypeClusterIP, 69 ClusterIP: "hit", 70 Ports: []v1.ServicePort{ 71 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 72 }, 73 }, 74 }, 75 }, 76 endpoints: matchingEndpoints, 77 78 clusterMode: expectation{error: true}, 79 endpointMode: expectation{error: true}, 80 }, 81 { 82 name: "cluster ip", 83 services: []*v1.Service{ 84 { 85 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 86 Spec: v1.ServiceSpec{ 87 Type: v1.ServiceTypeClusterIP, 88 ClusterIP: "hit", 89 Ports: []v1.ServicePort{ 90 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 91 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 92 }, 93 }, 94 }, 95 }, 96 endpoints: matchingEndpoints, 97 98 clusterMode: expectation{url: "https://hit:443"}, 99 endpointMode: expectation{url: "https://127.0.0.1:1443"}, 100 }, 101 { 102 name: "cluster ip without endpoints", 103 services: []*v1.Service{ 104 { 105 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 106 Spec: v1.ServiceSpec{ 107 Type: v1.ServiceTypeClusterIP, 108 ClusterIP: "hit", 109 Ports: []v1.ServicePort{ 110 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 111 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 112 }, 113 }, 114 }, 115 }, 116 endpoints: nil, 117 118 clusterMode: expectation{url: "https://hit:443"}, 119 endpointMode: expectation{error: true}, 120 }, 121 { 122 name: "endpoint without subset", 123 services: []*v1.Service{ 124 { 125 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 126 Spec: v1.ServiceSpec{ 127 Type: v1.ServiceTypeClusterIP, 128 ClusterIP: "hit", 129 Ports: []v1.ServicePort{ 130 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 131 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 132 }, 133 }, 134 }, 135 }, 136 endpoints: func(svc *v1.Service) []*v1.Endpoints { 137 return []*v1.Endpoints{{ 138 ObjectMeta: metav1.ObjectMeta{Namespace: svc.Namespace, Name: svc.Name}, 139 Subsets: []v1.EndpointSubset{}, 140 }} 141 }, 142 143 clusterMode: expectation{url: "https://hit:443"}, 144 endpointMode: expectation{error: true}, 145 }, 146 { 147 name: "endpoint subset without addresses", 148 services: []*v1.Service{ 149 { 150 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 151 Spec: v1.ServiceSpec{ 152 Type: v1.ServiceTypeClusterIP, 153 ClusterIP: "hit", 154 Ports: []v1.ServicePort{ 155 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 156 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 157 }, 158 }, 159 }, 160 }, 161 endpoints: func(svc *v1.Service) []*v1.Endpoints { 162 return []*v1.Endpoints{{ 163 ObjectMeta: metav1.ObjectMeta{Namespace: svc.Namespace, Name: svc.Name}, 164 Subsets: []v1.EndpointSubset{{ 165 Addresses: []v1.EndpointAddress{}, 166 Ports: []v1.EndpointPort{{Name: "https", Port: 443}}, 167 }}, 168 }} 169 }, 170 171 clusterMode: expectation{url: "https://hit:443"}, 172 endpointMode: expectation{error: true}, 173 }, 174 { 175 name: "none cluster ip", 176 services: []*v1.Service{ 177 { 178 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 179 Spec: v1.ServiceSpec{ 180 Type: v1.ServiceTypeClusterIP, 181 ClusterIP: v1.ClusterIPNone, 182 }, 183 }, 184 }, 185 endpoints: nil, 186 187 clusterMode: expectation{error: true}, 188 endpointMode: expectation{error: true}, 189 }, 190 { 191 name: "loadbalancer", 192 services: []*v1.Service{ 193 { 194 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 195 Spec: v1.ServiceSpec{ 196 Type: v1.ServiceTypeLoadBalancer, 197 ClusterIP: "lb", 198 Ports: []v1.ServicePort{ 199 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 200 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 201 }, 202 }, 203 }, 204 }, 205 endpoints: matchingEndpoints, 206 207 clusterMode: expectation{url: "https://lb:443"}, 208 endpointMode: expectation{url: "https://127.0.0.1:1443"}, 209 }, 210 { 211 name: "node port", 212 services: []*v1.Service{ 213 { 214 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 215 Spec: v1.ServiceSpec{ 216 Type: v1.ServiceTypeNodePort, 217 ClusterIP: "np", 218 Ports: []v1.ServicePort{ 219 {Name: "https", Port: 443, TargetPort: intstr.FromInt32(1443)}, 220 {Port: 1234, TargetPort: intstr.FromInt32(1234)}, 221 }, 222 }, 223 }, 224 }, 225 endpoints: matchingEndpoints, 226 227 clusterMode: expectation{url: "https://np:443"}, 228 endpointMode: expectation{url: "https://127.0.0.1:1443"}, 229 }, 230 { 231 name: "external name", 232 services: []*v1.Service{ 233 { 234 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 235 Spec: v1.ServiceSpec{ 236 Type: v1.ServiceTypeExternalName, 237 ExternalName: "foo.bar.com", 238 }, 239 }, 240 }, 241 endpoints: nil, 242 243 clusterMode: expectation{url: "https://foo.bar.com:443"}, 244 endpointMode: expectation{error: true}, 245 }, 246 { 247 name: "unsupported service", 248 services: []*v1.Service{ 249 { 250 ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"}, 251 Spec: v1.ServiceSpec{ 252 Type: "unsupported", 253 }, 254 }, 255 }, 256 endpoints: nil, 257 258 clusterMode: expectation{error: true}, 259 endpointMode: expectation{error: true}, 260 }, 261 { 262 name: "missing service", 263 services: nil, 264 endpoints: nil, 265 266 clusterMode: expectation{error: true}, 267 endpointMode: expectation{error: true}, 268 }, 269 } 270 271 for _, test := range tests { 272 serviceCache := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) 273 serviceLister := v1listers.NewServiceLister(serviceCache) 274 for i := range test.services { 275 if err := serviceCache.Add(test.services[i]); err != nil { 276 t.Fatalf("%s unexpected service add error: %v", test.name, err) 277 } 278 } 279 280 endpointCache := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) 281 endpointLister := v1listers.NewEndpointsLister(endpointCache) 282 if test.endpoints != nil { 283 for _, svc := range test.services { 284 for _, ep := range test.endpoints(svc) { 285 if err := endpointCache.Add(ep); err != nil { 286 t.Fatalf("%s unexpected endpoint add error: %v", test.name, err) 287 } 288 } 289 } 290 } 291 292 check := func(mode string, expected expectation, url *url.URL, err error) { 293 switch { 294 case err == nil && expected.error: 295 t.Errorf("%s in %s mode expected error, got none", test.name, mode) 296 case err != nil && expected.error: 297 // ignore 298 case err != nil: 299 t.Errorf("%s in %s mode unexpected error: %v", test.name, mode, err) 300 case url.String() != expected.url: 301 t.Errorf("%s in %s mode expected url %q, got %q", test.name, mode, expected.url, url.String()) 302 } 303 } 304 305 clusterURL, err := ResolveCluster(serviceLister, "one", "alfa", 443) 306 check("cluster", test.clusterMode, clusterURL, err) 307 308 endpointURL, err := ResolveEndpoint(serviceLister, endpointLister, "one", "alfa", 443) 309 check("endpoint", test.endpointMode, endpointURL, err) 310 } 311 }