From f47661e1b72d920a604cb1bab25518c59601b8b6 Mon Sep 17 00:00:00 2001 From: Lingyan Yin Date: Fri, 11 Aug 2023 14:31:24 -0700 Subject: [PATCH] fix inplace VPA stuck in InProgress when custom resources are specified added unit tests Co-authored-by: Shengjie Xue <3150104939@zju.edu.cn> Co-authored-by: Zewei Ding Co-authored-by: Jiaxin Shan --- pkg/kubelet/kubelet_pods.go | 20 +++++- pkg/kubelet/kubelet_pods_test.go | 112 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 03d8dc987b2..4a0f841763c 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -1625,12 +1625,30 @@ func getPhase(pod *v1.Pod, info []v1.ContainerStatus, podIsTerminal bool) v1.Pod } } +func deleteCustomResourceFromResourceRequirements(target *v1.ResourceRequirements) { + for resource := range target.Limits { + if resource != v1.ResourceCPU && resource != v1.ResourceMemory && resource != v1.ResourceEphemeralStorage { + delete(target.Limits, resource) + } + } + for resource := range target.Requests { + if resource != v1.ResourceCPU && resource != v1.ResourceMemory && resource != v1.ResourceEphemeralStorage { + delete(target.Requests, resource) + } + } +} + func (kl *Kubelet) determinePodResizeStatus(pod *v1.Pod, podStatus *v1.PodStatus) v1.PodResizeStatus { var podResizeStatus v1.PodResizeStatus specStatusDiffer := false for _, c := range pod.Spec.Containers { if cs, ok := podutil.GetContainerStatus(podStatus.ContainerStatuses, c.Name); ok { - if cs.Resources != nil && !cmp.Equal(c.Resources, *cs.Resources) { + cResourceCopy := c.Resources.DeepCopy() + // for both requests and limits, we only compare the cpu, memory and ephemeralstorage + // which are included in convertToAPIContainerStatuses + deleteCustomResourceFromResourceRequirements(cResourceCopy) + csResourceCopy := cs.Resources.DeepCopy() + if csResourceCopy != nil && !cmp.Equal(*cResourceCopy, *csResourceCopy) { specStatusDiffer = true break } diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 919946475bf..cb2a2768d1c 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -3781,6 +3781,118 @@ func Test_generateAPIPodStatus(t *testing.T) { } } +func Test_generateAPIPodStatusForInPlaceVPAEnabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)() + testContainerName := "ctr0" + testContainerID := kubecontainer.ContainerID{Type: "test", ID: testContainerName} + + CPU1AndMem1G := v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceMemory: resource.MustParse("1Gi")} + CPU1AndMem1GAndStorage2G := CPU1AndMem1G.DeepCopy() + CPU1AndMem1GAndStorage2G[v1.ResourceEphemeralStorage] = resource.MustParse("2Gi") + CPU1AndMem1GAndStorage2GAndCustomResource := CPU1AndMem1GAndStorage2G.DeepCopy() + CPU1AndMem1GAndStorage2GAndCustomResource["unknown-resource"] = resource.MustParse("1") + + testKubecontainerPodStatus := kubecontainer.PodStatus{ + ContainerStatuses: []*kubecontainer.Status{ + { + ID: testContainerID, + Name: testContainerName, + Resources: &kubecontainer.ContainerResources{ + CPURequest: CPU1AndMem1G.Cpu(), + MemoryRequest: CPU1AndMem1G.Memory(), + CPULimit: CPU1AndMem1G.Cpu(), + MemoryLimit: CPU1AndMem1G.Memory(), + }, + }, + }, + } + + tests := []struct { + name string + pod *v1.Pod + oldStatus *v1.PodStatus + }{ + { + name: "custom resource in ResourcesAllocated, resize should be null", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + UID: "1234560", + Name: "foo0", + Namespace: "bar0", + }, + Spec: v1.PodSpec{ + NodeName: "machine", + Containers: []v1.Container{ + { + Name: testContainerName, + Image: "img", + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2GAndCustomResource, Requests: CPU1AndMem1GAndStorage2GAndCustomResource}, + }, + }, + RestartPolicy: v1.RestartPolicyAlways, + }, + Status: v1.PodStatus{ + ContainerStatuses: []v1.ContainerStatus{ + { + Name: testContainerName, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + AllocatedResources: CPU1AndMem1GAndStorage2GAndCustomResource, + }, + }, + Resize: "InProgress", + }, + }, + }, + { + name: "cpu/memory resource in ResourcesAllocated, resize should be null", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + UID: "1234560", + Name: "foo0", + Namespace: "bar0", + }, + Spec: v1.PodSpec{ + NodeName: "machine", + Containers: []v1.Container{ + { + Name: testContainerName, + Image: "img", + Resources: v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + }, + }, + RestartPolicy: v1.RestartPolicyAlways, + }, + Status: v1.PodStatus{ + ContainerStatuses: []v1.ContainerStatus{ + { + Name: testContainerName, + Resources: &v1.ResourceRequirements{Limits: CPU1AndMem1GAndStorage2G, Requests: CPU1AndMem1GAndStorage2G}, + AllocatedResources: CPU1AndMem1GAndStorage2G, + }, + }, + Resize: "InProgress", + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) + defer testKubelet.Cleanup() + kl := testKubelet.kubelet + + oldStatus := test.pod.Status + kl.statusManager = status.NewFakeManager() + kl.statusManager.SetPodStatus(test.pod, oldStatus) + actual := kl.generateAPIPodStatus(test.pod, &testKubecontainerPodStatus /* criStatus */, false /* test.isPodTerminal */) + + if actual.Resize != "" { + t.Fatalf("Unexpected Resize status: %s", actual.Resize) + } + }) + } +} + func findContainerStatusByName(status v1.PodStatus, name string) *v1.ContainerStatus { for i, c := range status.InitContainerStatuses { if c.Name == name {