k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/helpers_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2015 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 cm 21 22 import ( 23 "reflect" 24 "strconv" 25 "testing" 26 "time" 27 28 v1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/resource" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 pkgfeatures "k8s.io/kubernetes/pkg/features" 33 ) 34 35 // getResourceList returns a ResourceList with the 36 // specified cpu and memory resource values 37 func getResourceList(cpu, memory string) v1.ResourceList { 38 res := v1.ResourceList{} 39 if cpu != "" { 40 res[v1.ResourceCPU] = resource.MustParse(cpu) 41 } 42 if memory != "" { 43 res[v1.ResourceMemory] = resource.MustParse(memory) 44 } 45 return res 46 } 47 48 // getResourceRequirements returns a ResourceRequirements object 49 func getResourceRequirements(requests, limits v1.ResourceList) v1.ResourceRequirements { 50 res := v1.ResourceRequirements{} 51 res.Requests = requests 52 res.Limits = limits 53 return res 54 } 55 56 func TestResourceConfigForPod(t *testing.T) { 57 defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds 58 tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond) // in microseconds 59 60 minShares := uint64(MinShares) 61 burstableShares := MilliCPUToShares(100) 62 memoryQuantity := resource.MustParse("200Mi") 63 burstableMemory := memoryQuantity.Value() 64 burstablePartialShares := MilliCPUToShares(200) 65 burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod)) 66 guaranteedShares := MilliCPUToShares(100) 67 guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod)) 68 guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod)) 69 memoryQuantity = resource.MustParse("100Mi") 70 cpuNoLimit := int64(-1) 71 guaranteedMemory := memoryQuantity.Value() 72 testCases := map[string]struct { 73 pod *v1.Pod 74 expected *ResourceConfig 75 enforceCPULimits bool 76 quotaPeriod uint64 // in microseconds 77 }{ 78 "besteffort": { 79 pod: &v1.Pod{ 80 Spec: v1.PodSpec{ 81 Containers: []v1.Container{ 82 { 83 Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")), 84 }, 85 }, 86 }, 87 }, 88 enforceCPULimits: true, 89 quotaPeriod: defaultQuotaPeriod, 90 expected: &ResourceConfig{CPUShares: &minShares}, 91 }, 92 "burstable-no-limits": { 93 pod: &v1.Pod{ 94 Spec: v1.PodSpec{ 95 Containers: []v1.Container{ 96 { 97 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 98 }, 99 }, 100 }, 101 }, 102 enforceCPULimits: true, 103 quotaPeriod: defaultQuotaPeriod, 104 expected: &ResourceConfig{CPUShares: &burstableShares}, 105 }, 106 "burstable-with-limits": { 107 pod: &v1.Pod{ 108 Spec: v1.PodSpec{ 109 Containers: []v1.Container{ 110 { 111 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 112 }, 113 }, 114 }, 115 }, 116 enforceCPULimits: true, 117 quotaPeriod: defaultQuotaPeriod, 118 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, 119 }, 120 "burstable-with-limits-no-cpu-enforcement": { 121 pod: &v1.Pod{ 122 Spec: v1.PodSpec{ 123 Containers: []v1.Container{ 124 { 125 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 126 }, 127 }, 128 }, 129 }, 130 enforceCPULimits: false, 131 quotaPeriod: defaultQuotaPeriod, 132 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, 133 }, 134 "burstable-partial-limits": { 135 pod: &v1.Pod{ 136 Spec: v1.PodSpec{ 137 Containers: []v1.Container{ 138 { 139 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 140 }, 141 { 142 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 143 }, 144 }, 145 }, 146 }, 147 enforceCPULimits: true, 148 quotaPeriod: defaultQuotaPeriod, 149 expected: &ResourceConfig{CPUShares: &burstablePartialShares}, 150 }, 151 "burstable-with-limits-with-tuned-quota": { 152 pod: &v1.Pod{ 153 Spec: v1.PodSpec{ 154 Containers: []v1.Container{ 155 { 156 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 157 }, 158 }, 159 }, 160 }, 161 enforceCPULimits: true, 162 quotaPeriod: tunedQuotaPeriod, 163 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory}, 164 }, 165 "burstable-with-limits-no-cpu-enforcement-with-tuned-quota": { 166 pod: &v1.Pod{ 167 Spec: v1.PodSpec{ 168 Containers: []v1.Container{ 169 { 170 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 171 }, 172 }, 173 }, 174 }, 175 enforceCPULimits: false, 176 quotaPeriod: tunedQuotaPeriod, 177 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory}, 178 }, 179 "burstable-partial-limits-with-tuned-quota": { 180 pod: &v1.Pod{ 181 Spec: v1.PodSpec{ 182 Containers: []v1.Container{ 183 { 184 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 185 }, 186 { 187 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 188 }, 189 }, 190 }, 191 }, 192 enforceCPULimits: true, 193 quotaPeriod: tunedQuotaPeriod, 194 expected: &ResourceConfig{CPUShares: &burstablePartialShares}, 195 }, 196 "guaranteed": { 197 pod: &v1.Pod{ 198 Spec: v1.PodSpec{ 199 Containers: []v1.Container{ 200 { 201 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 202 }, 203 }, 204 }, 205 }, 206 enforceCPULimits: true, 207 quotaPeriod: defaultQuotaPeriod, 208 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, 209 }, 210 "guaranteed-no-cpu-enforcement": { 211 pod: &v1.Pod{ 212 Spec: v1.PodSpec{ 213 Containers: []v1.Container{ 214 { 215 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 216 }, 217 }, 218 }, 219 }, 220 enforceCPULimits: false, 221 quotaPeriod: defaultQuotaPeriod, 222 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, 223 }, 224 "guaranteed-with-tuned-quota": { 225 pod: &v1.Pod{ 226 Spec: v1.PodSpec{ 227 Containers: []v1.Container{ 228 { 229 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 230 }, 231 }, 232 }, 233 }, 234 enforceCPULimits: true, 235 quotaPeriod: tunedQuotaPeriod, 236 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory}, 237 }, 238 "guaranteed-no-cpu-enforcement-with-tuned-quota": { 239 pod: &v1.Pod{ 240 Spec: v1.PodSpec{ 241 Containers: []v1.Container{ 242 { 243 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 244 }, 245 }, 246 }, 247 }, 248 enforceCPULimits: false, 249 quotaPeriod: tunedQuotaPeriod, 250 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory}, 251 }, 252 "burstable-partial-limits-with-init-containers": { 253 pod: &v1.Pod{ 254 Spec: v1.PodSpec{ 255 Containers: []v1.Container{ 256 { 257 Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("100m", "100Mi")), 258 }, 259 { 260 Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("", "")), 261 }, 262 }, 263 InitContainers: []v1.Container{ 264 { 265 Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("100m", "100Mi")), 266 }, 267 { 268 Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("", "")), 269 }, 270 }, 271 }, 272 }, 273 enforceCPULimits: true, 274 quotaPeriod: tunedQuotaPeriod, 275 expected: &ResourceConfig{CPUShares: &burstablePartialShares}, 276 }, 277 } 278 279 for testName, testCase := range testCases { 280 281 actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, false) 282 283 if !reflect.DeepEqual(actual.CPUPeriod, testCase.expected.CPUPeriod) { 284 t.Errorf("unexpected result, test: %v, cpu period not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUPeriod, *actual.CPUPeriod) 285 } 286 if !reflect.DeepEqual(actual.CPUQuota, testCase.expected.CPUQuota) { 287 t.Errorf("unexpected result, test: %v, cpu quota not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUQuota, *actual.CPUQuota) 288 } 289 if !reflect.DeepEqual(actual.CPUShares, testCase.expected.CPUShares) { 290 t.Errorf("unexpected result, test: %v, cpu shares not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUShares, &actual.CPUShares) 291 } 292 if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) { 293 t.Errorf("unexpected result, test: %v, memory not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.Memory, *actual.Memory) 294 } 295 } 296 } 297 298 func TestResourceConfigForPodWithCustomCPUCFSQuotaPeriod(t *testing.T) { 299 defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds 300 tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond) // in microseconds 301 tunedQuota := int64(1 * time.Millisecond / time.Microsecond) 302 303 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.CPUCFSQuotaPeriod, true)() 304 305 minShares := uint64(MinShares) 306 burstableShares := MilliCPUToShares(100) 307 memoryQuantity := resource.MustParse("200Mi") 308 burstableMemory := memoryQuantity.Value() 309 burstablePartialShares := MilliCPUToShares(200) 310 burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod)) 311 guaranteedShares := MilliCPUToShares(100) 312 guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod)) 313 guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod)) 314 memoryQuantity = resource.MustParse("100Mi") 315 cpuNoLimit := int64(-1) 316 guaranteedMemory := memoryQuantity.Value() 317 testCases := map[string]struct { 318 pod *v1.Pod 319 expected *ResourceConfig 320 enforceCPULimits bool 321 quotaPeriod uint64 // in microseconds 322 }{ 323 "besteffort": { 324 pod: &v1.Pod{ 325 Spec: v1.PodSpec{ 326 Containers: []v1.Container{ 327 { 328 Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")), 329 }, 330 }, 331 }, 332 }, 333 enforceCPULimits: true, 334 quotaPeriod: defaultQuotaPeriod, 335 expected: &ResourceConfig{CPUShares: &minShares}, 336 }, 337 "burstable-no-limits": { 338 pod: &v1.Pod{ 339 Spec: v1.PodSpec{ 340 Containers: []v1.Container{ 341 { 342 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 343 }, 344 }, 345 }, 346 }, 347 enforceCPULimits: true, 348 quotaPeriod: defaultQuotaPeriod, 349 expected: &ResourceConfig{CPUShares: &burstableShares}, 350 }, 351 "burstable-with-limits": { 352 pod: &v1.Pod{ 353 Spec: v1.PodSpec{ 354 Containers: []v1.Container{ 355 { 356 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 357 }, 358 }, 359 }, 360 }, 361 enforceCPULimits: true, 362 quotaPeriod: defaultQuotaPeriod, 363 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, 364 }, 365 "burstable-with-limits-no-cpu-enforcement": { 366 pod: &v1.Pod{ 367 Spec: v1.PodSpec{ 368 Containers: []v1.Container{ 369 { 370 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 371 }, 372 }, 373 }, 374 }, 375 enforceCPULimits: false, 376 quotaPeriod: defaultQuotaPeriod, 377 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, 378 }, 379 "burstable-partial-limits": { 380 pod: &v1.Pod{ 381 Spec: v1.PodSpec{ 382 Containers: []v1.Container{ 383 { 384 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 385 }, 386 { 387 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 388 }, 389 }, 390 }, 391 }, 392 enforceCPULimits: true, 393 quotaPeriod: defaultQuotaPeriod, 394 expected: &ResourceConfig{CPUShares: &burstablePartialShares}, 395 }, 396 "burstable-with-limits-with-tuned-quota": { 397 pod: &v1.Pod{ 398 Spec: v1.PodSpec{ 399 Containers: []v1.Container{ 400 { 401 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 402 }, 403 }, 404 }, 405 }, 406 enforceCPULimits: true, 407 quotaPeriod: tunedQuotaPeriod, 408 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &tunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory}, 409 }, 410 "burstable-with-limits-no-cpu-enforcement-with-tuned-quota": { 411 pod: &v1.Pod{ 412 Spec: v1.PodSpec{ 413 Containers: []v1.Container{ 414 { 415 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 416 }, 417 }, 418 }, 419 }, 420 enforceCPULimits: false, 421 quotaPeriod: tunedQuotaPeriod, 422 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory}, 423 }, 424 "burstable-partial-limits-with-tuned-quota": { 425 pod: &v1.Pod{ 426 Spec: v1.PodSpec{ 427 Containers: []v1.Container{ 428 { 429 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 430 }, 431 { 432 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 433 }, 434 }, 435 }, 436 }, 437 enforceCPULimits: true, 438 quotaPeriod: tunedQuotaPeriod, 439 expected: &ResourceConfig{CPUShares: &burstablePartialShares}, 440 }, 441 "guaranteed": { 442 pod: &v1.Pod{ 443 Spec: v1.PodSpec{ 444 Containers: []v1.Container{ 445 { 446 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 447 }, 448 }, 449 }, 450 }, 451 enforceCPULimits: true, 452 quotaPeriod: defaultQuotaPeriod, 453 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, 454 }, 455 "guaranteed-no-cpu-enforcement": { 456 pod: &v1.Pod{ 457 Spec: v1.PodSpec{ 458 Containers: []v1.Container{ 459 { 460 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 461 }, 462 }, 463 }, 464 }, 465 enforceCPULimits: false, 466 quotaPeriod: defaultQuotaPeriod, 467 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, 468 }, 469 "guaranteed-with-tuned-quota": { 470 pod: &v1.Pod{ 471 Spec: v1.PodSpec{ 472 Containers: []v1.Container{ 473 { 474 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 475 }, 476 }, 477 }, 478 }, 479 enforceCPULimits: true, 480 quotaPeriod: tunedQuotaPeriod, 481 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory}, 482 }, 483 "guaranteed-no-cpu-enforcement-with-tuned-quota": { 484 pod: &v1.Pod{ 485 Spec: v1.PodSpec{ 486 Containers: []v1.Container{ 487 { 488 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 489 }, 490 }, 491 }, 492 }, 493 enforceCPULimits: false, 494 quotaPeriod: tunedQuotaPeriod, 495 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory}, 496 }, 497 } 498 499 for testName, testCase := range testCases { 500 501 actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, false) 502 503 if !reflect.DeepEqual(actual.CPUPeriod, testCase.expected.CPUPeriod) { 504 t.Errorf("unexpected result, test: %v, cpu period not as expected", testName) 505 } 506 if !reflect.DeepEqual(actual.CPUQuota, testCase.expected.CPUQuota) { 507 t.Errorf("unexpected result, test: %v, cpu quota not as expected", testName) 508 } 509 if !reflect.DeepEqual(actual.CPUShares, testCase.expected.CPUShares) { 510 t.Errorf("unexpected result, test: %v, cpu shares not as expected", testName) 511 } 512 if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) { 513 t.Errorf("unexpected result, test: %v, memory not as expected", testName) 514 } 515 } 516 } 517 518 func TestMilliCPUToQuota(t *testing.T) { 519 testCases := []struct { 520 input int64 521 quota int64 522 period uint64 523 }{ 524 { 525 input: int64(0), 526 quota: int64(0), 527 period: uint64(0), 528 }, 529 { 530 input: int64(5), 531 quota: int64(1000), 532 period: uint64(100000), 533 }, 534 { 535 input: int64(9), 536 quota: int64(1000), 537 period: uint64(100000), 538 }, 539 { 540 input: int64(10), 541 quota: int64(1000), 542 period: uint64(100000), 543 }, 544 { 545 input: int64(200), 546 quota: int64(20000), 547 period: uint64(100000), 548 }, 549 { 550 input: int64(500), 551 quota: int64(50000), 552 period: uint64(100000), 553 }, 554 { 555 input: int64(1000), 556 quota: int64(100000), 557 period: uint64(100000), 558 }, 559 { 560 input: int64(1500), 561 quota: int64(150000), 562 period: uint64(100000), 563 }, 564 } 565 for _, testCase := range testCases { 566 quota := MilliCPUToQuota(testCase.input, int64(testCase.period)) 567 if quota != testCase.quota { 568 t.Errorf("Input %v and %v, expected quota %v, but got quota %v", testCase.input, testCase.period, testCase.quota, quota) 569 } 570 } 571 } 572 573 func TestHugePageLimits(t *testing.T) { 574 Mi := int64(1024 * 1024) 575 type inputStruct struct { 576 key string 577 input string 578 } 579 580 testCases := []struct { 581 name string 582 inputs []inputStruct 583 expected map[int64]int64 584 }{ 585 { 586 name: "no valid hugepages", 587 inputs: []inputStruct{ 588 { 589 key: "2Mi", 590 input: "128", 591 }, 592 }, 593 expected: map[int64]int64{}, 594 }, 595 { 596 name: "2Mi only", 597 inputs: []inputStruct{ 598 { 599 key: v1.ResourceHugePagesPrefix + "2Mi", 600 input: "128", 601 }, 602 }, 603 expected: map[int64]int64{2 * Mi: 128}, 604 }, 605 { 606 name: "2Mi and 4Mi", 607 inputs: []inputStruct{ 608 { 609 key: v1.ResourceHugePagesPrefix + "2Mi", 610 input: "128", 611 }, 612 { 613 key: v1.ResourceHugePagesPrefix + strconv.FormatInt(2*Mi, 10), 614 input: "256", 615 }, 616 { 617 key: v1.ResourceHugePagesPrefix + "4Mi", 618 input: "512", 619 }, 620 { 621 key: "4Mi", 622 input: "1024", 623 }, 624 }, 625 expected: map[int64]int64{2 * Mi: 384, 4 * Mi: 512}, 626 }, 627 } 628 629 for _, testcase := range testCases { 630 t.Run(testcase.name, func(t *testing.T) { 631 resourceList := v1.ResourceList{} 632 633 for _, input := range testcase.inputs { 634 value, err := resource.ParseQuantity(input.input) 635 if err != nil { 636 t.Fatalf("error in parsing hugepages, value: %s", input.input) 637 } else { 638 resourceList[v1.ResourceName(input.key)] = value 639 } 640 } 641 642 resultValue := HugePageLimits(resourceList) 643 644 if !reflect.DeepEqual(testcase.expected, resultValue) { 645 t.Errorf("unexpected result for HugePageLimits(), expected: %v, actual: %v", testcase.expected, resultValue) 646 } 647 648 // ensure ResourceConfigForPod uses HugePageLimits correctly internally 649 p := v1.Pod{ 650 Spec: v1.PodSpec{ 651 Containers: []v1.Container{ 652 { 653 Resources: v1.ResourceRequirements{ 654 Requests: resourceList, 655 }, 656 }, 657 }, 658 }, 659 } 660 resultValuePod := ResourceConfigForPod(&p, false, 0, false) 661 if !reflect.DeepEqual(testcase.expected, resultValuePod.HugePageLimit) { 662 t.Errorf("unexpected result for ResourceConfigForPod(), expected: %v, actual: %v", testcase.expected, resultValuePod) 663 } 664 }) 665 } 666 } 667 668 func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) { 669 defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds 670 tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond) // in microseconds 671 672 minShares := uint64(MinShares) 673 burstableShares := MilliCPUToShares(100) 674 memoryQuantity := resource.MustParse("200Mi") 675 burstableMemory := memoryQuantity.Value() 676 burstablePartialShares := MilliCPUToShares(200) 677 burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod)) 678 guaranteedShares := MilliCPUToShares(100) 679 guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod)) 680 guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod)) 681 memoryQuantity = resource.MustParse("100Mi") 682 cpuNoLimit := int64(-1) 683 guaranteedMemory := memoryQuantity.Value() 684 testCases := map[string]struct { 685 pod *v1.Pod 686 expected *ResourceConfig 687 enforceCPULimits bool 688 quotaPeriod uint64 // in microseconds 689 }{ 690 "besteffort": { 691 pod: &v1.Pod{ 692 Spec: v1.PodSpec{ 693 Containers: []v1.Container{ 694 { 695 Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")), 696 }, 697 }, 698 }, 699 }, 700 enforceCPULimits: true, 701 quotaPeriod: defaultQuotaPeriod, 702 expected: &ResourceConfig{CPUShares: &minShares}, 703 }, 704 "burstable-no-limits": { 705 pod: &v1.Pod{ 706 Spec: v1.PodSpec{ 707 Containers: []v1.Container{ 708 { 709 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 710 }, 711 }, 712 }, 713 }, 714 enforceCPULimits: true, 715 quotaPeriod: defaultQuotaPeriod, 716 expected: &ResourceConfig{CPUShares: &burstableShares, Unified: map[string]string{"memory.min": "104857600"}}, 717 }, 718 "burstable-with-limits": { 719 pod: &v1.Pod{ 720 Spec: v1.PodSpec{ 721 Containers: []v1.Container{ 722 { 723 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 724 }, 725 }, 726 }, 727 }, 728 enforceCPULimits: true, 729 quotaPeriod: defaultQuotaPeriod, 730 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}}, 731 }, 732 "burstable-with-limits-no-cpu-enforcement": { 733 pod: &v1.Pod{ 734 Spec: v1.PodSpec{ 735 Containers: []v1.Container{ 736 { 737 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 738 }, 739 }, 740 }, 741 }, 742 enforceCPULimits: false, 743 quotaPeriod: defaultQuotaPeriod, 744 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}}, 745 }, 746 "burstable-partial-limits": { 747 pod: &v1.Pod{ 748 Spec: v1.PodSpec{ 749 Containers: []v1.Container{ 750 { 751 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 752 }, 753 { 754 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 755 }, 756 }, 757 }, 758 }, 759 enforceCPULimits: true, 760 quotaPeriod: defaultQuotaPeriod, 761 expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}}, 762 }, 763 "burstable-with-limits-with-tuned-quota": { 764 pod: &v1.Pod{ 765 Spec: v1.PodSpec{ 766 Containers: []v1.Container{ 767 { 768 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 769 }, 770 }, 771 }, 772 }, 773 enforceCPULimits: true, 774 quotaPeriod: tunedQuotaPeriod, 775 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}}, 776 }, 777 "burstable-with-limits-no-cpu-enforcement-with-tuned-quota": { 778 pod: &v1.Pod{ 779 Spec: v1.PodSpec{ 780 Containers: []v1.Container{ 781 { 782 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 783 }, 784 }, 785 }, 786 }, 787 enforceCPULimits: false, 788 quotaPeriod: tunedQuotaPeriod, 789 expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}}, 790 }, 791 "burstable-partial-limits-with-tuned-quota": { 792 pod: &v1.Pod{ 793 Spec: v1.PodSpec{ 794 Containers: []v1.Container{ 795 { 796 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")), 797 }, 798 { 799 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")), 800 }, 801 }, 802 }, 803 }, 804 enforceCPULimits: true, 805 quotaPeriod: tunedQuotaPeriod, 806 expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}}, 807 }, 808 "guaranteed": { 809 pod: &v1.Pod{ 810 Spec: v1.PodSpec{ 811 Containers: []v1.Container{ 812 { 813 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 814 }, 815 }, 816 }, 817 }, 818 enforceCPULimits: true, 819 quotaPeriod: defaultQuotaPeriod, 820 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}}, 821 }, 822 "guaranteed-no-cpu-enforcement": { 823 pod: &v1.Pod{ 824 Spec: v1.PodSpec{ 825 Containers: []v1.Container{ 826 { 827 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 828 }, 829 }, 830 }, 831 }, 832 enforceCPULimits: false, 833 quotaPeriod: defaultQuotaPeriod, 834 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}}, 835 }, 836 "guaranteed-with-tuned-quota": { 837 pod: &v1.Pod{ 838 Spec: v1.PodSpec{ 839 Containers: []v1.Container{ 840 { 841 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 842 }, 843 }, 844 }, 845 }, 846 enforceCPULimits: true, 847 quotaPeriod: tunedQuotaPeriod, 848 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}}, 849 }, 850 "guaranteed-no-cpu-enforcement-with-tuned-quota": { 851 pod: &v1.Pod{ 852 Spec: v1.PodSpec{ 853 Containers: []v1.Container{ 854 { 855 Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")), 856 }, 857 }, 858 }, 859 }, 860 enforceCPULimits: false, 861 quotaPeriod: tunedQuotaPeriod, 862 expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}}, 863 }, 864 } 865 866 for testName, testCase := range testCases { 867 868 actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, true) 869 870 if !reflect.DeepEqual(actual.Unified, testCase.expected.Unified) { 871 t.Errorf("unexpected result, test: %v, unified not as expected", testName) 872 } 873 } 874 }