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  }