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 }