k8s.io/kubernetes@v1.29.3/pkg/quota/v1/evaluator/core/services_test.go (about)

     1  /*
     2  Copyright 2016 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 core
    18  
    19  import (
    20  	"testing"
    21  
    22  	corev1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/api/resource"
    24  	"k8s.io/apimachinery/pkg/runtime/schema"
    25  	quota "k8s.io/apiserver/pkg/quota/v1"
    26  	"k8s.io/apiserver/pkg/quota/v1/generic"
    27  	api "k8s.io/kubernetes/pkg/apis/core"
    28  	utilpointer "k8s.io/utils/pointer"
    29  )
    30  
    31  func TestServiceEvaluatorMatchesResources(t *testing.T) {
    32  	evaluator := NewServiceEvaluator(nil)
    33  	// we give a lot of resources
    34  	input := []corev1.ResourceName{
    35  		corev1.ResourceConfigMaps,
    36  		corev1.ResourceCPU,
    37  		corev1.ResourceServices,
    38  		corev1.ResourceServicesNodePorts,
    39  		corev1.ResourceServicesLoadBalancers,
    40  	}
    41  	// but we only match these...
    42  	expected := quota.ToSet([]corev1.ResourceName{
    43  		corev1.ResourceServices,
    44  		corev1.ResourceServicesNodePorts,
    45  		corev1.ResourceServicesLoadBalancers,
    46  	})
    47  	actual := quota.ToSet(evaluator.MatchingResources(input))
    48  	if !expected.Equal(actual) {
    49  		t.Errorf("expected: %v, actual: %v", expected, actual)
    50  	}
    51  }
    52  
    53  func TestServiceEvaluatorUsage(t *testing.T) {
    54  	evaluator := NewServiceEvaluator(nil)
    55  	testCases := map[string]struct {
    56  		service *api.Service
    57  		usage   corev1.ResourceList
    58  	}{
    59  		"loadbalancer": {
    60  			service: &api.Service{
    61  				Spec: api.ServiceSpec{
    62  					Type: api.ServiceTypeLoadBalancer,
    63  				},
    64  			},
    65  			usage: corev1.ResourceList{
    66  				corev1.ResourceServicesNodePorts:     resource.MustParse("0"),
    67  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
    68  				corev1.ResourceServices:              resource.MustParse("1"),
    69  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
    70  			},
    71  		},
    72  		"loadbalancer_ports": {
    73  			service: &api.Service{
    74  				Spec: api.ServiceSpec{
    75  					Type: api.ServiceTypeLoadBalancer,
    76  					Ports: []api.ServicePort{
    77  						{
    78  							Port: 27443,
    79  						},
    80  					},
    81  				},
    82  			},
    83  			usage: corev1.ResourceList{
    84  				corev1.ResourceServicesNodePorts:     resource.MustParse("1"),
    85  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
    86  				corev1.ResourceServices:              resource.MustParse("1"),
    87  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
    88  			},
    89  		},
    90  		"loadbalancer_2_ports": {
    91  			service: &api.Service{
    92  				Spec: api.ServiceSpec{
    93  					Type: api.ServiceTypeLoadBalancer,
    94  					Ports: []api.ServicePort{
    95  						{
    96  							Port: 27443,
    97  						},
    98  						{
    99  							Port: 27444,
   100  						},
   101  					},
   102  				},
   103  			},
   104  			usage: corev1.ResourceList{
   105  				corev1.ResourceServicesNodePorts:     resource.MustParse("2"),
   106  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
   107  				corev1.ResourceServices:              resource.MustParse("1"),
   108  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   109  			},
   110  		},
   111  		"clusterip": {
   112  			service: &api.Service{
   113  				Spec: api.ServiceSpec{
   114  					Type: api.ServiceTypeClusterIP,
   115  				},
   116  			},
   117  			usage: corev1.ResourceList{
   118  				corev1.ResourceServices:              resource.MustParse("1"),
   119  				corev1.ResourceServicesNodePorts:     resource.MustParse("0"),
   120  				corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
   121  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   122  			},
   123  		},
   124  		"nodeports": {
   125  			service: &api.Service{
   126  				Spec: api.ServiceSpec{
   127  					Type: api.ServiceTypeNodePort,
   128  					Ports: []api.ServicePort{
   129  						{
   130  							Port: 27443,
   131  						},
   132  					},
   133  				},
   134  			},
   135  			usage: corev1.ResourceList{
   136  				corev1.ResourceServices:              resource.MustParse("1"),
   137  				corev1.ResourceServicesNodePorts:     resource.MustParse("1"),
   138  				corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
   139  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   140  			},
   141  		},
   142  		"multi-nodeports": {
   143  			service: &api.Service{
   144  				Spec: api.ServiceSpec{
   145  					Type: api.ServiceTypeNodePort,
   146  					Ports: []api.ServicePort{
   147  						{
   148  							Port: 27443,
   149  						},
   150  						{
   151  							Port: 27444,
   152  						},
   153  					},
   154  				},
   155  			},
   156  			usage: corev1.ResourceList{
   157  				corev1.ResourceServices:              resource.MustParse("1"),
   158  				corev1.ResourceServicesNodePorts:     resource.MustParse("2"),
   159  				corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
   160  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   161  			},
   162  		},
   163  		"nodeports-disabled": {
   164  			service: &api.Service{
   165  				Spec: api.ServiceSpec{
   166  					Type: api.ServiceTypeLoadBalancer,
   167  					Ports: []api.ServicePort{
   168  						{
   169  							Port: 27443,
   170  						},
   171  						{
   172  							Port: 27444,
   173  						},
   174  					},
   175  					AllocateLoadBalancerNodePorts: utilpointer.BoolPtr(false),
   176  				},
   177  			},
   178  			usage: corev1.ResourceList{
   179  				corev1.ResourceServices:              resource.MustParse("1"),
   180  				corev1.ResourceServicesNodePorts:     resource.MustParse("0"),
   181  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
   182  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   183  			},
   184  		},
   185  		"nodeports-default-enabled": {
   186  			service: &api.Service{
   187  				Spec: api.ServiceSpec{
   188  					Type: api.ServiceTypeLoadBalancer,
   189  					Ports: []api.ServicePort{
   190  						{
   191  							Port:     27443,
   192  							NodePort: 32001,
   193  						},
   194  						{
   195  							Port:     27444,
   196  							NodePort: 32002,
   197  						},
   198  					},
   199  					AllocateLoadBalancerNodePorts: nil,
   200  				},
   201  			},
   202  			usage: corev1.ResourceList{
   203  				corev1.ResourceServices:              resource.MustParse("1"),
   204  				corev1.ResourceServicesNodePorts:     resource.MustParse("2"),
   205  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
   206  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   207  			},
   208  		},
   209  		"nodeports-explicitly-enabled": {
   210  			service: &api.Service{
   211  				Spec: api.ServiceSpec{
   212  					Type: api.ServiceTypeLoadBalancer,
   213  					Ports: []api.ServicePort{
   214  						{
   215  							Port: 27443,
   216  						},
   217  						{
   218  							Port: 27444,
   219  						},
   220  					},
   221  					AllocateLoadBalancerNodePorts: utilpointer.BoolPtr(true),
   222  				},
   223  			},
   224  			usage: corev1.ResourceList{
   225  				corev1.ResourceServices:              resource.MustParse("1"),
   226  				corev1.ResourceServicesNodePorts:     resource.MustParse("2"),
   227  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
   228  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   229  			},
   230  		},
   231  		"nodeports-disabled-but-specified": {
   232  			service: &api.Service{
   233  				Spec: api.ServiceSpec{
   234  					Type: api.ServiceTypeLoadBalancer,
   235  					Ports: []api.ServicePort{
   236  						{
   237  							Port:     27443,
   238  							NodePort: 32001,
   239  						},
   240  						{
   241  							Port:     27444,
   242  							NodePort: 32002,
   243  						},
   244  					},
   245  					AllocateLoadBalancerNodePorts: utilpointer.BoolPtr(false),
   246  				},
   247  			},
   248  			usage: corev1.ResourceList{
   249  				corev1.ResourceServices:              resource.MustParse("1"),
   250  				corev1.ResourceServicesNodePorts:     resource.MustParse("2"),
   251  				corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
   252  				generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
   253  			},
   254  		},
   255  	}
   256  	for testName, testCase := range testCases {
   257  		t.Run(testName, func(t *testing.T) {
   258  			actual, err := evaluator.Usage(testCase.service)
   259  			if err != nil {
   260  				t.Errorf("%s unexpected error: %v", testName, err)
   261  			}
   262  			if !quota.Equals(testCase.usage, actual) {
   263  				t.Errorf("%s expected: %v, actual: %v", testName, testCase.usage, actual)
   264  			}
   265  		})
   266  	}
   267  }
   268  
   269  func TestServiceConstraintsFunc(t *testing.T) {
   270  	testCases := map[string]struct {
   271  		service  *api.Service
   272  		required []corev1.ResourceName
   273  		err      string
   274  	}{
   275  		"loadbalancer": {
   276  			service: &api.Service{
   277  				Spec: api.ServiceSpec{
   278  					Type: api.ServiceTypeLoadBalancer,
   279  				},
   280  			},
   281  			required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers},
   282  		},
   283  		"clusterip": {
   284  			service: &api.Service{
   285  				Spec: api.ServiceSpec{
   286  					Type: api.ServiceTypeClusterIP,
   287  				},
   288  			},
   289  			required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers, corev1.ResourceServices},
   290  		},
   291  		"nodeports": {
   292  			service: &api.Service{
   293  				Spec: api.ServiceSpec{
   294  					Type: api.ServiceTypeNodePort,
   295  					Ports: []api.ServicePort{
   296  						{
   297  							Port: 27443,
   298  						},
   299  					},
   300  				},
   301  			},
   302  			required: []corev1.ResourceName{corev1.ResourceServicesNodePorts},
   303  		},
   304  		"multi-nodeports": {
   305  			service: &api.Service{
   306  				Spec: api.ServiceSpec{
   307  					Type: api.ServiceTypeNodePort,
   308  					Ports: []api.ServicePort{
   309  						{
   310  							Port: 27443,
   311  						},
   312  						{
   313  							Port: 27444,
   314  						},
   315  					},
   316  				},
   317  			},
   318  			required: []corev1.ResourceName{corev1.ResourceServicesNodePorts},
   319  		},
   320  	}
   321  
   322  	evaluator := NewServiceEvaluator(nil)
   323  	for testName, test := range testCases {
   324  		err := evaluator.Constraints(test.required, test.service)
   325  		switch {
   326  		case err != nil && len(test.err) == 0,
   327  			err == nil && len(test.err) != 0,
   328  			err != nil && test.err != err.Error():
   329  			t.Errorf("%s unexpected error: %v", testName, err)
   330  		}
   331  	}
   332  }