From 4eead9306d59b9897578eab2f968fcafd4402844 Mon Sep 17 00:00:00 2001 From: Natasha Sarkar Date: Fri, 3 Oct 2025 19:32:18 +0000 Subject: [PATCH] add coverage for pod resize 'read' and 'replace' endpoints --- test/e2e/common/node/pod_resize.go | 98 +++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/test/e2e/common/node/pod_resize.go b/test/e2e/common/node/pod_resize.go index ec225dd4d1d..501c1731883 100644 --- a/test/e2e/common/node/pod_resize.go +++ b/test/e2e/common/node/pod_resize.go @@ -24,8 +24,10 @@ import ( "time" v1 "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/e2e/common/node/framework/cgroups" @@ -35,6 +37,7 @@ import ( e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" + "github.com/google/go-cmp/cmp" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -301,7 +304,6 @@ func doBurstablePodResizeTests(f *framework.Framework) { true, ), ) - } func doPodResizePatchErrorTests(f *framework.Framework) { @@ -656,14 +658,93 @@ func doPodResizeMemoryLimitDecreaseTest(f *framework.Framework) { }) } -// NOTE: Pod resize scheduler resource quota tests are out of scope in e2e_node tests, -// because in e2e_node tests -// a) scheduler and controller manager is not running by the Node e2e -// b) api-server in services doesn't start with --enable-admission-plugins=ResourceQuota -// and is not possible to start it from TEST_ARGS -// Above tests are performed by doSheduletTests() and doPodResizeResourceQuotaTests() -// in test/e2e/node/pod_resize.go +func doPodResizeReadAndReplaceTests(f *framework.Framework) { + // This test provides basic coverage for the `read` and `replace` endpoints. + // TODO: Promote to conformance prior to GA. + ginkgo.It("resize pod via the replace endpoint", func(ctx context.Context) { + podClient := e2epod.NewPodClient(f) + original := []podresize.ResizableContainerInfo{{ + Name: "c1", + Resources: &cgroups.ContainerResources{CPUReq: originalCPU, MemReq: originalMem}, + }} + desiredContainers := []podresize.ResizableContainerInfo{{ + Name: "c1", + Resources: &cgroups.ContainerResources{CPUReq: increasedCPU, MemReq: increasedMem}, + }} + desired := v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse(increasedCPU), + v1.ResourceMemory: resource.MustParse(increasedMem), + }, + } + ginkgo.By("creating and verifying pod") + pod := createAndVerifyPod(ctx, f, podClient, original) + gomega.Expect(pod.Generation).To(gomega.BeEquivalentTo(1)) + + podToUpdate, err := podClient.Get(ctx, pod.Name, metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to get pod") + podToUpdate.Spec.Containers[0].Resources = desired + + ginkgo.By("updating the pod resources") + _, err = podClient.UpdateResize(ctx, pod.Name, podToUpdate, metav1.UpdateOptions{}) + framework.ExpectNoError(err, "failed to resize pod") + + ginkgo.By("fetching updated pod") + updatedPod, err := podClient.Get(ctx, pod.Name, metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to get pod") + + ginkgo.By("verifying pod resources") + podresize.VerifyPodResources(updatedPod, desiredContainers) + gomega.Expect(updatedPod.Generation).To(gomega.BeEquivalentTo(2)) + + ginkgo.By("verifying pod resources after patch") + expected := podresize.UpdateExpectedContainerRestarts(ctx, updatedPod, desiredContainers) + resizedPod := podresize.WaitForPodResizeActuation(ctx, f, podClient, updatedPod, expected) + podresize.ExpectPodResized(ctx, f, resizedPod, expected) + + ginkgo.By("verifying pod fetched from resize subresource") + framework.ExpectNoError(framework.Gomega(). + Eventually(ctx, framework.RetryNotFound(framework.GetObject(f.ClientSet.CoreV1().Pods(pod.Namespace).Get, pod.Name, metav1.GetOptions{}))). + WithTimeout(f.Timeouts.PodStart). + Should(framework.MakeMatcher(func(pod *v1.Pod) (func() string, error) { + // For some reason, the pod object returned from the client doesn't have the GVK populated. + pod.Kind = "Pod" + pod.APIVersion = "v1" + + ginkgo.By("verifying pod fetched from resize subresource") + podResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} + unstruct, err := f.DynamicClient.Resource(podResource).Namespace(f.Namespace.Name).Get(ctx, pod.Name, metav1.GetOptions{}, "resize") + if err != nil { + return func() string { + return fmt.Sprintf("failed to fetch pod after resize: %v", err.Error()) + }, nil + } + updatedPodFromResize, err := unstructuredToPod(unstruct) + if err != nil { + return func() string { + return fmt.Sprintf("couldn't convert result to pod object: %v", err.Error()) + }, nil + } + if !apiequality.Semantic.DeepEqual(pod, updatedPodFromResize) { + return func() string { + return fmt.Sprintf("pod from resize subresource not equivalent to pod; %s", cmp.Diff(pod, updatedPodFromResize)) + }, nil + } + + return nil, nil + })), + ) + }) +} + +// NOTE: Pod resize scheduler, resource quota, limit ranger, deferred resize tests are out of scope in e2e_node tests, +// +// because in e2e_node tests +// a) scheduler and controller manager is not running by the Node e2e +// b) api-server in services doesn't start with --enable-admission-plugins=ResourceQuota +// and is not possible to start it from TEST_ARGS +// Above tests in test/e2e/node/pod_resize.go var _ = SIGDescribe("Pod InPlace Resize Container", framework.WithFeatureGate(features.InPlacePodVerticalScaling), func() { f := framework.NewDefaultFramework("pod-resize-tests") @@ -677,6 +758,7 @@ var _ = SIGDescribe("Pod InPlace Resize Container", framework.WithFeatureGate(fe doGuaranteedPodResizeTests(f) doBurstablePodResizeTests(f) + doPodResizeReadAndReplaceTests(f) doPodResizePatchErrorTests(f) doPodResizeMemoryLimitDecreaseTest(f) })