k8s.io/kubernetes@v1.29.3/test/e2e_node/memory_manager_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2017 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 e2enode 21 22 import ( 23 "context" 24 "encoding/json" 25 "fmt" 26 "os" 27 "os/exec" 28 "regexp" 29 "sort" 30 "strconv" 31 "strings" 32 "time" 33 34 v1 "k8s.io/api/core/v1" 35 "k8s.io/apimachinery/pkg/api/resource" 36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 kubeletpodresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" 38 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 39 "k8s.io/kubernetes/pkg/kubelet/apis/podresources" 40 "k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state" 41 "k8s.io/kubernetes/pkg/kubelet/util" 42 "k8s.io/kubernetes/test/e2e/feature" 43 "k8s.io/kubernetes/test/e2e/framework" 44 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 45 admissionapi "k8s.io/pod-security-admission/api" 46 "k8s.io/utils/cpuset" 47 "k8s.io/utils/pointer" 48 49 "github.com/onsi/ginkgo/v2" 50 "github.com/onsi/gomega" 51 ) 52 53 const ( 54 evictionHardMemory = "memory.available" 55 resourceMemory = "memory" 56 staticPolicy = "Static" 57 nonePolicy = "None" 58 hugepages2MiCount = 8 59 ) 60 61 // Helper for makeMemoryManagerPod(). 62 type memoryManagerCtnAttributes struct { 63 ctnName string 64 cpus string 65 memory string 66 hugepages2Mi string 67 } 68 69 // makeMemoryManagerContainers returns slice of containers with provided attributes and indicator of hugepages mount needed for those. 70 func makeMemoryManagerContainers(ctnCmd string, ctnAttributes []memoryManagerCtnAttributes) ([]v1.Container, bool) { 71 hugepagesMount := false 72 var containers []v1.Container 73 for _, ctnAttr := range ctnAttributes { 74 ctn := v1.Container{ 75 Name: ctnAttr.ctnName, 76 Image: busyboxImage, 77 Resources: v1.ResourceRequirements{ 78 Limits: v1.ResourceList{ 79 v1.ResourceCPU: resource.MustParse(ctnAttr.cpus), 80 v1.ResourceMemory: resource.MustParse(ctnAttr.memory), 81 }, 82 }, 83 Command: []string{"sh", "-c", ctnCmd}, 84 } 85 if ctnAttr.hugepages2Mi != "" { 86 hugepagesMount = true 87 88 ctn.Resources.Limits[hugepagesResourceName2Mi] = resource.MustParse(ctnAttr.hugepages2Mi) 89 ctn.VolumeMounts = []v1.VolumeMount{ 90 { 91 Name: "hugepages-2mi", 92 MountPath: "/hugepages-2Mi", 93 }, 94 } 95 } 96 97 containers = append(containers, ctn) 98 } 99 100 return containers, hugepagesMount 101 } 102 103 // makeMemoryMangerPod returns a pod with the provided ctnAttributes. 104 func makeMemoryManagerPod(podName string, initCtnAttributes, ctnAttributes []memoryManagerCtnAttributes) *v1.Pod { 105 hugepagesMount := false 106 memsetCmd := "grep Mems_allowed_list /proc/self/status | cut -f2" 107 memsetSleepCmd := memsetCmd + "&& sleep 1d" 108 var containers, initContainers []v1.Container 109 if len(initCtnAttributes) > 0 { 110 initContainers, _ = makeMemoryManagerContainers(memsetCmd, initCtnAttributes) 111 } 112 containers, hugepagesMount = makeMemoryManagerContainers(memsetSleepCmd, ctnAttributes) 113 114 pod := &v1.Pod{ 115 ObjectMeta: metav1.ObjectMeta{ 116 GenerateName: podName, 117 }, 118 Spec: v1.PodSpec{ 119 RestartPolicy: v1.RestartPolicyNever, 120 Containers: containers, 121 InitContainers: initContainers, 122 }, 123 } 124 125 if hugepagesMount { 126 pod.Spec.Volumes = []v1.Volume{ 127 { 128 Name: "hugepages-2mi", 129 VolumeSource: v1.VolumeSource{ 130 EmptyDir: &v1.EmptyDirVolumeSource{ 131 Medium: mediumHugepages2Mi, 132 }, 133 }, 134 }, 135 } 136 } 137 138 return pod 139 } 140 141 func getMemoryManagerState() (*state.MemoryManagerCheckpoint, error) { 142 if _, err := os.Stat(memoryManagerStateFile); os.IsNotExist(err) { 143 return nil, fmt.Errorf("the memory manager state file %s does not exist", memoryManagerStateFile) 144 } 145 146 out, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("cat %s", memoryManagerStateFile)).Output() 147 if err != nil { 148 return nil, fmt.Errorf("failed to run command 'cat %s': out: %s, err: %w", memoryManagerStateFile, out, err) 149 } 150 151 memoryManagerCheckpoint := &state.MemoryManagerCheckpoint{} 152 if err := json.Unmarshal(out, memoryManagerCheckpoint); err != nil { 153 return nil, fmt.Errorf("failed to unmarshal memory manager state file: %w", err) 154 } 155 return memoryManagerCheckpoint, nil 156 } 157 158 func getAllocatableMemoryFromStateFile(s *state.MemoryManagerCheckpoint) []state.Block { 159 var allocatableMemory []state.Block 160 for numaNodeID, numaNodeState := range s.MachineState { 161 for resourceName, memoryTable := range numaNodeState.MemoryMap { 162 if memoryTable.Allocatable == 0 { 163 continue 164 } 165 166 block := state.Block{ 167 NUMAAffinity: []int{numaNodeID}, 168 Type: resourceName, 169 Size: memoryTable.Allocatable, 170 } 171 allocatableMemory = append(allocatableMemory, block) 172 } 173 } 174 return allocatableMemory 175 } 176 177 type kubeletParams struct { 178 memoryManagerPolicy string 179 systemReservedMemory []kubeletconfig.MemoryReservation 180 systemReserved map[string]string 181 kubeReserved map[string]string 182 evictionHard map[string]string 183 } 184 185 func updateKubeletConfigWithMemoryManagerParams(initialCfg *kubeletconfig.KubeletConfiguration, params *kubeletParams) { 186 if initialCfg.FeatureGates == nil { 187 initialCfg.FeatureGates = map[string]bool{} 188 } 189 190 initialCfg.MemoryManagerPolicy = params.memoryManagerPolicy 191 192 // update system-reserved 193 if initialCfg.SystemReserved == nil { 194 initialCfg.SystemReserved = map[string]string{} 195 } 196 for resourceName, value := range params.systemReserved { 197 initialCfg.SystemReserved[resourceName] = value 198 } 199 200 // update kube-reserved 201 if initialCfg.KubeReserved == nil { 202 initialCfg.KubeReserved = map[string]string{} 203 } 204 for resourceName, value := range params.kubeReserved { 205 initialCfg.KubeReserved[resourceName] = value 206 } 207 208 // update hard eviction threshold 209 if initialCfg.EvictionHard == nil { 210 initialCfg.EvictionHard = map[string]string{} 211 } 212 for resourceName, value := range params.evictionHard { 213 initialCfg.EvictionHard[resourceName] = value 214 } 215 216 // update reserved memory 217 if initialCfg.ReservedMemory == nil { 218 initialCfg.ReservedMemory = []kubeletconfig.MemoryReservation{} 219 } 220 initialCfg.ReservedMemory = append(initialCfg.ReservedMemory, params.systemReservedMemory...) 221 } 222 223 func getAllNUMANodes() []int { 224 outData, err := exec.Command("/bin/sh", "-c", "lscpu").Output() 225 framework.ExpectNoError(err) 226 227 numaNodeRegex, err := regexp.Compile(`NUMA node(\d+) CPU\(s\):`) 228 framework.ExpectNoError(err) 229 230 matches := numaNodeRegex.FindAllSubmatch(outData, -1) 231 232 var numaNodes []int 233 for _, m := range matches { 234 n, err := strconv.Atoi(string(m[1])) 235 framework.ExpectNoError(err) 236 237 numaNodes = append(numaNodes, n) 238 } 239 240 sort.Ints(numaNodes) 241 return numaNodes 242 } 243 244 // Serial because the test updates kubelet configuration. 245 var _ = SIGDescribe("Memory Manager", framework.WithDisruptive(), framework.WithSerial(), feature.MemoryManager, func() { 246 // TODO: add more complex tests that will include interaction between CPUManager, MemoryManager and TopologyManager 247 var ( 248 allNUMANodes []int 249 ctnParams, initCtnParams []memoryManagerCtnAttributes 250 is2MiHugepagesSupported *bool 251 isMultiNUMASupported *bool 252 testPod *v1.Pod 253 ) 254 255 f := framework.NewDefaultFramework("memory-manager-test") 256 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 257 258 memoryQuantity := resource.MustParse("1100Mi") 259 defaultKubeParams := &kubeletParams{ 260 systemReservedMemory: []kubeletconfig.MemoryReservation{ 261 { 262 NumaNode: 0, 263 Limits: v1.ResourceList{ 264 resourceMemory: memoryQuantity, 265 }, 266 }, 267 }, 268 systemReserved: map[string]string{resourceMemory: "500Mi"}, 269 kubeReserved: map[string]string{resourceMemory: "500Mi"}, 270 evictionHard: map[string]string{evictionHardMemory: "100Mi"}, 271 } 272 273 verifyMemoryPinning := func(ctx context.Context, pod *v1.Pod, numaNodeIDs []int) { 274 ginkgo.By("Verifying the NUMA pinning") 275 276 output, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name) 277 framework.ExpectNoError(err) 278 279 currentNUMANodeIDs, err := cpuset.Parse(strings.Trim(output, "\n")) 280 framework.ExpectNoError(err) 281 282 gomega.Expect(numaNodeIDs).To(gomega.Equal(currentNUMANodeIDs.List())) 283 } 284 285 waitingForHugepages := func(ctx context.Context, hugepagesCount int) { 286 gomega.Eventually(ctx, func(ctx context.Context) error { 287 node, err := f.ClientSet.CoreV1().Nodes().Get(ctx, framework.TestContext.NodeName, metav1.GetOptions{}) 288 if err != nil { 289 return err 290 } 291 292 capacity, ok := node.Status.Capacity[v1.ResourceName(hugepagesResourceName2Mi)] 293 if !ok { 294 return fmt.Errorf("the node does not have the resource %s", hugepagesResourceName2Mi) 295 } 296 297 size, succeed := capacity.AsInt64() 298 if !succeed { 299 return fmt.Errorf("failed to convert quantity to int64") 300 } 301 302 // 512 Mb, the expected size in bytes 303 expectedSize := int64(hugepagesCount * hugepagesSize2M * 1024) 304 if size != expectedSize { 305 return fmt.Errorf("the actual size %d is different from the expected one %d", size, expectedSize) 306 } 307 return nil 308 }, time.Minute, framework.Poll).Should(gomega.BeNil()) 309 } 310 311 ginkgo.BeforeEach(func(ctx context.Context) { 312 if isMultiNUMASupported == nil { 313 isMultiNUMASupported = pointer.BoolPtr(isMultiNUMA()) 314 } 315 316 if is2MiHugepagesSupported == nil { 317 is2MiHugepagesSupported = pointer.BoolPtr(isHugePageAvailable(hugepagesSize2M)) 318 } 319 320 if len(allNUMANodes) == 0 { 321 allNUMANodes = getAllNUMANodes() 322 } 323 324 // allocate hugepages 325 if *is2MiHugepagesSupported { 326 ginkgo.By("Configuring hugepages") 327 gomega.Eventually(ctx, func() error { 328 return configureHugePages(hugepagesSize2M, hugepages2MiCount, pointer.IntPtr(0)) 329 }, 30*time.Second, framework.Poll).Should(gomega.BeNil()) 330 } 331 }) 332 333 // dynamically update the kubelet configuration 334 ginkgo.JustBeforeEach(func(ctx context.Context) { 335 // allocate hugepages 336 if *is2MiHugepagesSupported { 337 ginkgo.By("Waiting for hugepages resource to become available on the local node") 338 waitingForHugepages(ctx, hugepages2MiCount) 339 340 for i := 0; i < len(ctnParams); i++ { 341 ctnParams[i].hugepages2Mi = "8Mi" 342 } 343 } 344 345 if len(ctnParams) > 0 { 346 testPod = makeMemoryManagerPod(ctnParams[0].ctnName, initCtnParams, ctnParams) 347 } 348 }) 349 350 ginkgo.JustAfterEach(func(ctx context.Context) { 351 // delete the test pod 352 if testPod != nil && testPod.Name != "" { 353 e2epod.NewPodClient(f).DeleteSync(ctx, testPod.Name, metav1.DeleteOptions{}, 2*time.Minute) 354 } 355 356 // release hugepages 357 if *is2MiHugepagesSupported { 358 ginkgo.By("Releasing allocated hugepages") 359 gomega.Eventually(ctx, func() error { 360 // configure hugepages on the NUMA node 0 to avoid hugepages split across NUMA nodes 361 return configureHugePages(hugepagesSize2M, 0, pointer.IntPtr(0)) 362 }, 90*time.Second, 15*time.Second).ShouldNot(gomega.HaveOccurred(), "failed to release hugepages") 363 } 364 }) 365 366 ginkgo.Context("with static policy", func() { 367 tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) { 368 kubeParams := *defaultKubeParams 369 kubeParams.memoryManagerPolicy = staticPolicy 370 updateKubeletConfigWithMemoryManagerParams(initialConfig, &kubeParams) 371 }) 372 373 ginkgo.JustAfterEach(func() { 374 // reset containers attributes 375 ctnParams = []memoryManagerCtnAttributes{} 376 initCtnParams = []memoryManagerCtnAttributes{} 377 }) 378 379 // TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945 380 ginkgo.It("should report memory data during request to pod resources GetAllocatableResources", func(ctx context.Context) { 381 endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket) 382 framework.ExpectNoError(err) 383 384 cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize) 385 framework.ExpectNoError(err) 386 defer conn.Close() 387 388 resp, err := cli.GetAllocatableResources(ctx, &kubeletpodresourcesv1.AllocatableResourcesRequest{}) 389 framework.ExpectNoError(err) 390 gomega.Expect(resp.Memory).ToNot(gomega.BeEmpty()) 391 392 stateData, err := getMemoryManagerState() 393 framework.ExpectNoError(err) 394 395 stateAllocatableMemory := getAllocatableMemoryFromStateFile(stateData) 396 gomega.Expect(resp.Memory).To(gomega.HaveLen(len(stateAllocatableMemory))) 397 398 for _, containerMemory := range resp.Memory { 399 gomega.Expect(containerMemory.Topology).NotTo(gomega.BeNil()) 400 gomega.Expect(containerMemory.Topology.Nodes).To(gomega.HaveLen(1)) 401 gomega.Expect(containerMemory.Topology.Nodes[0]).NotTo(gomega.BeNil()) 402 403 numaNodeID := int(containerMemory.Topology.Nodes[0].ID) 404 for _, numaStateMemory := range stateAllocatableMemory { 405 gomega.Expect(numaStateMemory.NUMAAffinity).To(gomega.HaveLen(1)) 406 if numaNodeID != numaStateMemory.NUMAAffinity[0] { 407 continue 408 } 409 410 if containerMemory.MemoryType != string(numaStateMemory.Type) { 411 continue 412 } 413 414 gomega.Expect(containerMemory.Size_).To(gomega.BeEquivalentTo(numaStateMemory.Size)) 415 } 416 } 417 418 gomega.Expect(resp.Memory).ToNot(gomega.BeEmpty()) 419 }) 420 421 ginkgo.When("guaranteed pod has init and app containers", func() { 422 ginkgo.BeforeEach(func() { 423 // override containers parameters 424 ctnParams = []memoryManagerCtnAttributes{ 425 { 426 ctnName: "memory-manager-static", 427 cpus: "100m", 428 memory: "128Mi", 429 }, 430 } 431 // override init container parameters 432 initCtnParams = []memoryManagerCtnAttributes{ 433 { 434 ctnName: "init-memory-manager-static", 435 cpus: "100m", 436 memory: "128Mi", 437 }, 438 } 439 }) 440 441 ginkgo.It("should succeed to start the pod", func(ctx context.Context) { 442 ginkgo.By("Running the test pod") 443 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 444 445 // it no taste to verify NUMA pinning when the node has only one NUMA node 446 if !*isMultiNUMASupported { 447 return 448 } 449 450 verifyMemoryPinning(ctx, testPod, []int{0}) 451 }) 452 }) 453 454 ginkgo.When("guaranteed pod has only app containers", func() { 455 ginkgo.BeforeEach(func() { 456 // override containers parameters 457 ctnParams = []memoryManagerCtnAttributes{ 458 { 459 ctnName: "memory-manager-static", 460 cpus: "100m", 461 memory: "128Mi", 462 }, 463 } 464 }) 465 466 ginkgo.It("should succeed to start the pod", func(ctx context.Context) { 467 ginkgo.By("Running the test pod") 468 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 469 470 // it no taste to verify NUMA pinning when the node has only one NUMA node 471 if !*isMultiNUMASupported { 472 return 473 } 474 475 verifyMemoryPinning(ctx, testPod, []int{0}) 476 }) 477 }) 478 479 ginkgo.When("multiple guaranteed pods started", func() { 480 var testPod2 *v1.Pod 481 482 ginkgo.BeforeEach(func() { 483 // override containers parameters 484 ctnParams = []memoryManagerCtnAttributes{ 485 { 486 ctnName: "memory-manager-static", 487 cpus: "100m", 488 memory: "128Mi", 489 }, 490 } 491 }) 492 493 ginkgo.JustBeforeEach(func() { 494 testPod2 = makeMemoryManagerPod("memory-manager-static", initCtnParams, ctnParams) 495 }) 496 497 ginkgo.It("should succeed to start all pods", func(ctx context.Context) { 498 ginkgo.By("Running the test pod and the test pod 2") 499 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 500 501 ginkgo.By("Running the test pod 2") 502 testPod2 = e2epod.NewPodClient(f).CreateSync(ctx, testPod2) 503 504 // it no taste to verify NUMA pinning when the node has only one NUMA node 505 if !*isMultiNUMASupported { 506 return 507 } 508 509 verifyMemoryPinning(ctx, testPod, []int{0}) 510 verifyMemoryPinning(ctx, testPod2, []int{0}) 511 }) 512 513 // TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945 514 ginkgo.It("should report memory data for each guaranteed pod and container during request to pod resources List", func(ctx context.Context) { 515 ginkgo.By("Running the test pod and the test pod 2") 516 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 517 518 ginkgo.By("Running the test pod 2") 519 testPod2 = e2epod.NewPodClient(f).CreateSync(ctx, testPod2) 520 521 endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket) 522 framework.ExpectNoError(err) 523 524 cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize) 525 framework.ExpectNoError(err) 526 defer conn.Close() 527 528 resp, err := cli.List(ctx, &kubeletpodresourcesv1.ListPodResourcesRequest{}) 529 framework.ExpectNoError(err) 530 531 for _, pod := range []*v1.Pod{testPod, testPod2} { 532 for _, podResource := range resp.PodResources { 533 if podResource.Name != pod.Name { 534 continue 535 } 536 537 for _, c := range pod.Spec.Containers { 538 for _, containerResource := range podResource.Containers { 539 if containerResource.Name != c.Name { 540 continue 541 } 542 543 for _, containerMemory := range containerResource.Memory { 544 q := c.Resources.Limits[v1.ResourceName(containerMemory.MemoryType)] 545 value, ok := q.AsInt64() 546 gomega.Expect(ok).To(gomega.BeTrue()) 547 gomega.Expect(value).To(gomega.BeEquivalentTo(containerMemory.Size_)) 548 } 549 } 550 } 551 } 552 } 553 }) 554 555 ginkgo.JustAfterEach(func(ctx context.Context) { 556 // delete the test pod 2 557 if testPod2.Name != "" { 558 e2epod.NewPodClient(f).DeleteSync(ctx, testPod2.Name, metav1.DeleteOptions{}, 2*time.Minute) 559 } 560 }) 561 }) 562 563 // the test requires at least two NUMA nodes 564 // test on each NUMA node will start the pod that will consume almost all memory of the NUMA node except 256Mi 565 // after it will start an additional pod with the memory request that can not be satisfied by the single NUMA node 566 // free memory 567 ginkgo.When("guaranteed pod memory request is bigger than free memory on each NUMA node", func() { 568 var workloadPods []*v1.Pod 569 570 ginkgo.BeforeEach(func() { 571 if !*isMultiNUMASupported { 572 ginkgo.Skip("The machines has less than two NUMA nodes") 573 } 574 575 ctnParams = []memoryManagerCtnAttributes{ 576 { 577 ctnName: "memory-manager-static", 578 cpus: "100m", 579 memory: "384Mi", 580 }, 581 } 582 }) 583 584 ginkgo.JustBeforeEach(func(ctx context.Context) { 585 stateData, err := getMemoryManagerState() 586 framework.ExpectNoError(err) 587 588 for _, memoryState := range stateData.MachineState { 589 // consume all memory except of 256Mi on each NUMA node via workload pods 590 workloadPodMemory := memoryState.MemoryMap[v1.ResourceMemory].Free - 256*1024*1024 591 memoryQuantity := resource.NewQuantity(int64(workloadPodMemory), resource.BinarySI) 592 workloadCtnAttrs := []memoryManagerCtnAttributes{ 593 { 594 ctnName: "workload-pod", 595 cpus: "100m", 596 memory: memoryQuantity.String(), 597 }, 598 } 599 workloadPod := makeMemoryManagerPod(workloadCtnAttrs[0].ctnName, initCtnParams, workloadCtnAttrs) 600 601 workloadPod = e2epod.NewPodClient(f).CreateSync(ctx, workloadPod) 602 workloadPods = append(workloadPods, workloadPod) 603 } 604 }) 605 606 ginkgo.It("should be rejected", func(ctx context.Context) { 607 ginkgo.By("Creating the pod") 608 testPod = e2epod.NewPodClient(f).Create(ctx, testPod) 609 610 ginkgo.By("Checking that pod failed to start because of admission error") 611 gomega.Eventually(ctx, func() bool { 612 tmpPod, err := e2epod.NewPodClient(f).Get(ctx, testPod.Name, metav1.GetOptions{}) 613 framework.ExpectNoError(err) 614 615 if tmpPod.Status.Phase != v1.PodFailed { 616 return false 617 } 618 619 if tmpPod.Status.Reason != "UnexpectedAdmissionError" { 620 return false 621 } 622 623 if !strings.Contains(tmpPod.Status.Message, "Allocate failed due to [memorymanager]") { 624 return false 625 } 626 627 return true 628 }, time.Minute, 5*time.Second).Should( 629 gomega.BeTrue(), 630 "the pod succeeded to start, when it should fail with the admission error", 631 ) 632 }) 633 634 ginkgo.JustAfterEach(func(ctx context.Context) { 635 for _, workloadPod := range workloadPods { 636 if workloadPod.Name != "" { 637 e2epod.NewPodClient(f).DeleteSync(ctx, workloadPod.Name, metav1.DeleteOptions{}, 2*time.Minute) 638 } 639 } 640 }) 641 }) 642 }) 643 644 ginkgo.Context("with none policy", func() { 645 tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) { 646 kubeParams := *defaultKubeParams 647 kubeParams.memoryManagerPolicy = nonePolicy 648 updateKubeletConfigWithMemoryManagerParams(initialConfig, &kubeParams) 649 }) 650 651 // empty context to configure same container parameters for all tests 652 ginkgo.Context("", func() { 653 ginkgo.BeforeEach(func() { 654 // override pod parameters 655 ctnParams = []memoryManagerCtnAttributes{ 656 { 657 ctnName: "memory-manager-none", 658 cpus: "100m", 659 memory: "128Mi", 660 }, 661 } 662 }) 663 664 // TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945 665 ginkgo.It("should not report any memory data during request to pod resources GetAllocatableResources", func(ctx context.Context) { 666 endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket) 667 framework.ExpectNoError(err) 668 669 cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize) 670 framework.ExpectNoError(err) 671 defer conn.Close() 672 673 resp, err := cli.GetAllocatableResources(ctx, &kubeletpodresourcesv1.AllocatableResourcesRequest{}) 674 framework.ExpectNoError(err) 675 676 gomega.Expect(resp.Memory).To(gomega.BeEmpty()) 677 }) 678 679 // TODO: move the test to pod resource API test suite, see - https://github.com/kubernetes/kubernetes/issues/101945 680 ginkgo.It("should not report any memory data during request to pod resources List", func(ctx context.Context) { 681 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 682 683 endpoint, err := util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket) 684 framework.ExpectNoError(err) 685 686 cli, conn, err := podresources.GetV1Client(endpoint, defaultPodResourcesTimeout, defaultPodResourcesMaxSize) 687 framework.ExpectNoError(err) 688 defer conn.Close() 689 690 resp, err := cli.List(ctx, &kubeletpodresourcesv1.ListPodResourcesRequest{}) 691 framework.ExpectNoError(err) 692 693 for _, podResource := range resp.PodResources { 694 if podResource.Name != testPod.Name { 695 continue 696 } 697 698 for _, containerResource := range podResource.Containers { 699 gomega.Expect(containerResource.Memory).To(gomega.BeEmpty()) 700 } 701 } 702 }) 703 704 ginkgo.It("should succeed to start the pod", func(ctx context.Context) { 705 testPod = e2epod.NewPodClient(f).CreateSync(ctx, testPod) 706 707 // it no taste to verify NUMA pinning when the node has only one NUMA node 708 if !*isMultiNUMASupported { 709 return 710 } 711 712 verifyMemoryPinning(ctx, testPod, allNUMANodes) 713 }) 714 }) 715 }) 716 })