Skip to content

Commit

Permalink
fix inplace VPA stuck in InProgress when custom resources are specified
Browse files Browse the repository at this point in the history
added unit tests

Co-authored-by: Shengjie Xue <3150104939@zju.edu.cn>
Co-authored-by: Zewei Ding <horace.d@outlook.com>
Co-authored-by: Jiaxin Shan <seedjeffwan@gmail.com>
  • Loading branch information
4 people authored and richabanker committed Jan 8, 2024
1 parent 050dbf7 commit 788fa27
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 1 deletion.
20 changes: 19 additions & 1 deletion pkg/kubelet/kubelet_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
112 changes: 112 additions & 0 deletions pkg/kubelet/kubelet_pods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 788fa27

Please sign in to comment.