diff --git a/vertical-pod-autoscaler/e2e/v1/common.go b/vertical-pod-autoscaler/e2e/v1/common.go index 6ce493c9a8b..ec313ff27d2 100644 --- a/vertical-pod-autoscaler/e2e/v1/common.go +++ b/vertical-pod-autoscaler/e2e/v1/common.go @@ -315,6 +315,13 @@ func InstallVPA(f *framework.Framework, vpa *vpa_types.VerticalPodAutoscaler) { } } +func JSONPatchDeployment(f *framework.Framework, deployment *appsv1.Deployment, patch *patchRecord) { + patchBytes, err := json.Marshal([]patchRecord{*patch}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + _, err = f.ClientSet.AppsV1().Deployments(f.Namespace.Name).Patch(context.TODO(), deployment.Name, types.JSONPatchType, patchBytes, metav1.PatchOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "unexpected error patching deployment") +} + func isStatusEmpty(status *vpa_types.VerticalPodAutoscalerStatus) bool { if status == nil { return true diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index 8f171f33f5d..f80f2e80299 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -411,6 +411,103 @@ var _ = RecommenderE2eDescribe("VPA CRD object", func() { }) }) +const recommendationLoopInterval = 1 * time.Minute + +var _ = RecommenderE2eDescribe("VPA CRD object", func() { + f := framework.NewDefaultFramework("vertical-pod-autoscaling") + f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline + + var vpaClientSet vpa_clientset.Interface + + ginkgo.BeforeEach(func() { + vpaClientSet = getVpaClientSet(f) + }) + + ginkgo.It("only provides recommendation to containers that exist when renaming a container", func() { + ginkgo.By("Setting up a hamster deployment") + d := NewNHamstersDeployment(f, 1 /*number of containers*/) + _ = startDeploymentPods(f, d) + + ginkgo.By("Setting up VPA CRD") + vpaCRD := test.VerticalPodAutoscaler(). + WithName("hamster-vpa"). + WithNamespace(f.Namespace.Name). + WithTargetRef(hamsterTargetRef). + WithContainer("*"). + Get() + + InstallVPA(f, vpaCRD) + + ginkgo.By("Waiting for recommendation to be filled for the container") + vpa, err := WaitForRecommendationPresent(vpaClientSet, vpaCRD) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1)) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations[0].ContainerName).To(gomega.Equal(GetHamsterContainerNameByIndex(0))) + + ginkgo.By("Renaming the container") + newContainerName := "renamed-container" + patchRecord := &patchRecord{ + Op: "replace", + Path: "/spec/template/spec/containers/0/name", + Value: newContainerName, + } + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + JSONPatchDeployment(f, d, patchRecord) + + ginkgo.By("Waiting for recommendation to be filled for the renamed container and only the renamed container") + time.Sleep(recommendationLoopInterval) + vpa, err = WaitForRecommendationPresent(vpaClientSet, vpaCRD) + + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + errMsg := fmt.Sprintf("%s is the only container in the VPA CR. There should not be any recommendations for %s", + newContainerName, + GetHamsterContainerNameByIndex(0)) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1), errMsg) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations[0].ContainerName).To(gomega.Equal(newContainerName), errMsg) + }) + + ginkgo.It("only provides recommendation to containers that exist when removing a container", func() { + ginkgo.By("Setting up a hamster deployment") + d := NewNHamstersDeployment(f, 2 /*number of containers*/) + _ = startDeploymentPods(f, d) + + ginkgo.By("Setting up VPA CRD") + vpaCRD := test.VerticalPodAutoscaler(). + WithName("hamster-vpa"). + WithNamespace(f.Namespace.Name). + WithTargetRef(hamsterTargetRef). + WithContainer("*"). + Get() + + InstallVPA(f, vpaCRD) + + ginkgo.By("Waiting for recommendation to be filled for both containers") + vpa, err := WaitForRecommendationPresent(vpaClientSet, vpaCRD) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(2)) + + ginkgo.By("Removing the second container") + patchRecord := &patchRecord{ + Op: "remove", + Path: "/spec/template/spec/containers/0", + } + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + JSONPatchDeployment(f, d, patchRecord) + + ginkgo.By("Waiting for recommendation to be filled for just one container") + time.Sleep(recommendationLoopInterval) + vpa, err = WaitForRecommendationPresent(vpaClientSet, vpaCRD) + + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + errMsg := fmt.Sprintf("%s is now the only container in the VPA CR. There should not be any recommendations for %s", + GetHamsterContainerNameByIndex(0), + GetHamsterContainerNameByIndex(1)) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1), errMsg) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations[0].ContainerName).To(gomega.Equal(GetHamsterContainerNameByIndex(0)), errMsg) + }) + +}) + func deleteRecommender(c clientset.Interface) error { namespace := "kube-system" listOptions := metav1.ListOptions{}