Skip to content

Commit c3c339c

Browse files
authored
Merge pull request #8348 from phuhung273/vpa-recommender-namespace-test
chore: add recommender vpa-object-namespace integration test
2 parents 1311229 + ff7874d commit c3c339c

File tree

12 files changed

+935
-439
lines changed

12 files changed

+935
-439
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package integration
18+
19+
import (
20+
"testing"
21+
22+
ginkgo "github.com/onsi/ginkgo/v2"
23+
"github.com/onsi/gomega"
24+
25+
runtimeutils "k8s.io/apimachinery/pkg/util/runtime"
26+
"k8s.io/component-base/logs"
27+
"k8s.io/kubernetes/test/e2e/framework"
28+
)
29+
30+
// RunE2ETests checks configuration parameters (specified through flags) and then runs
31+
// E2E tests using the Ginkgo runner.
32+
// If a "report directory" is specified, one or more JUnit test reports will be
33+
// generated in this directory, and cluster logs will also be saved.
34+
// This function is called on each Ginkgo node in parallel mode.
35+
func RunE2ETests(t *testing.T) {
36+
runtimeutils.ReallyCrash = true
37+
logs.InitLogs()
38+
defer logs.FlushLogs()
39+
40+
gomega.RegisterFailHandler(framework.Fail)
41+
suiteConfig, _ := ginkgo.GinkgoConfiguration()
42+
// Disable skipped tests unless they are explicitly requested.
43+
if len(suiteConfig.FocusStrings) == 0 && len(suiteConfig.SkipStrings) == 0 {
44+
suiteConfig.SkipStrings = []string{`\[Flaky\]|\[Feature:.+\]`}
45+
}
46+
ginkgo.RunSpecs(t, "Kubernetes e2e suite")
47+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package integration
18+
19+
import (
20+
"flag"
21+
"os"
22+
"testing"
23+
24+
"k8s.io/kubernetes/test/e2e/framework"
25+
"k8s.io/kubernetes/test/e2e/framework/config"
26+
)
27+
28+
// handleFlags sets up all flags and parses the command line.
29+
func handleFlags() {
30+
config.CopyFlags(config.Flags, flag.CommandLine)
31+
framework.RegisterCommonFlags(flag.CommandLine)
32+
framework.RegisterClusterFlags(flag.CommandLine)
33+
flag.Parse()
34+
}
35+
36+
func TestMain(m *testing.M) {
37+
// Register test flags, then parse flags.
38+
handleFlags()
39+
40+
framework.AfterReadingAllFlags(&framework.TestContext)
41+
42+
os.Exit(m.Run())
43+
}
44+
45+
func TestIntegration(t *testing.T) {
46+
RunE2ETests(t)
47+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package integration
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/autoscaler/vertical-pod-autoscaler/e2e/utils"
25+
26+
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
27+
vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
28+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test"
29+
"k8s.io/kubernetes/test/e2e/framework"
30+
podsecurity "k8s.io/pod-security-admission/api"
31+
32+
ginkgo "github.com/onsi/ginkgo/v2"
33+
"github.com/onsi/gomega"
34+
)
35+
36+
var _ = utils.RecommenderE2eDescribe("Flags", func() {
37+
f := framework.NewDefaultFramework("vertical-pod-autoscaling")
38+
f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline
39+
40+
var vpaClientSet vpa_clientset.Interface
41+
var hamsterNamespace string
42+
43+
ginkgo.BeforeEach(func() {
44+
vpaClientSet = utils.GetVpaClientSet(f)
45+
hamsterNamespace = f.Namespace.Name
46+
})
47+
48+
ginkgo.AfterEach(func() {
49+
f.ClientSet.AppsV1().Deployments(utils.RecommenderNamespace).Delete(context.TODO(), utils.RecommenderDeploymentName, metav1.DeleteOptions{})
50+
})
51+
52+
ginkgo.It("starts recommender with --vpa-object-namespace parameter", func() {
53+
ginkgo.By("Setting up VPA deployment")
54+
ignoredNamespace, err := f.CreateNamespace(context.TODO(), "ignored-namespace", nil)
55+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
56+
57+
f.Namespace.Name = utils.RecommenderNamespace
58+
vpaDeployment := utils.NewVPADeployment(f, []string{
59+
"--recommender-interval=10s",
60+
fmt.Sprintf("--vpa-object-namespace=%s", hamsterNamespace),
61+
})
62+
utils.StartDeploymentPods(f, vpaDeployment)
63+
64+
testIncludedAndIgnoredNamespaces(f, vpaClientSet, hamsterNamespace, ignoredNamespace.Name)
65+
})
66+
67+
ginkgo.It("starts recommender with --ignored-vpa-object-namespaces parameter", func() {
68+
ginkgo.By("Setting up VPA deployment")
69+
ignoredNamespace, err := f.CreateNamespace(context.TODO(), "ignored-namespace", nil)
70+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
71+
72+
f.Namespace.Name = utils.RecommenderNamespace
73+
vpaDeployment := utils.NewVPADeployment(f, []string{
74+
"--recommender-interval=10s",
75+
fmt.Sprintf("--ignored-vpa-object-namespaces=%s", ignoredNamespace.Name),
76+
})
77+
utils.StartDeploymentPods(f, vpaDeployment)
78+
79+
testIncludedAndIgnoredNamespaces(f, vpaClientSet, hamsterNamespace, ignoredNamespace.Name)
80+
})
81+
})
82+
83+
// Create VPA and deployment in 2 namespaces, 1 should be ignored
84+
// Ignored namespace VPA and deployment are intentionally created first
85+
// so that by the time included namespace has recommendation generated,
86+
// we know that ignored namespace has been waiting long enough.
87+
func testIncludedAndIgnoredNamespaces(f *framework.Framework, vpaClientSet vpa_clientset.Interface, includedNamespace, ignoredNamespace string) {
88+
ginkgo.By("Setting up a hamster deployment in ignored namespace")
89+
f.Namespace.Name = ignoredNamespace
90+
d := utils.NewNHamstersDeployment(f, 2)
91+
_ = utils.StartDeploymentPods(f, d)
92+
93+
ginkgo.By("Setting up VPA for ignored namespace")
94+
container1Name := utils.GetHamsterContainerNameByIndex(0)
95+
container2Name := utils.GetHamsterContainerNameByIndex(1)
96+
ignoredVpaCRD := test.VerticalPodAutoscaler().
97+
WithName("hamster-vpa").
98+
WithNamespace(ignoredNamespace).
99+
WithTargetRef(utils.HamsterTargetRef).
100+
WithContainer(container1Name).
101+
WithScalingMode(container1Name, vpa_types.ContainerScalingModeOff).
102+
WithContainer(container2Name).
103+
Get()
104+
f.Namespace.Name = ignoredNamespace
105+
utils.InstallVPA(f, ignoredVpaCRD)
106+
107+
ginkgo.By("Setting up a hamster deployment in included namespace")
108+
f.Namespace.Name = includedNamespace
109+
d = utils.NewNHamstersDeployment(f, 2)
110+
_ = utils.StartDeploymentPods(f, d)
111+
112+
ginkgo.By("Setting up VPA for included namespace")
113+
vpaCRD := test.VerticalPodAutoscaler().
114+
WithName("hamster-vpa").
115+
WithNamespace(includedNamespace).
116+
WithTargetRef(utils.HamsterTargetRef).
117+
WithContainer(container1Name).
118+
WithScalingMode(container1Name, vpa_types.ContainerScalingModeOff).
119+
WithContainer(container2Name).
120+
Get()
121+
122+
f.Namespace.Name = includedNamespace
123+
utils.InstallVPA(f, vpaCRD)
124+
125+
ginkgo.By("Waiting for recommendation to be filled for just one container")
126+
vpa, err := utils.WaitForRecommendationPresent(vpaClientSet, vpaCRD)
127+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
128+
errMsg := fmt.Sprintf("%s container has recommendations turned off. We expect expect only recommendations for %s",
129+
utils.GetHamsterContainerNameByIndex(0),
130+
utils.GetHamsterContainerNameByIndex(1))
131+
gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1), errMsg)
132+
gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations[0].ContainerName).To(gomega.Equal(utils.GetHamsterContainerNameByIndex(1)), errMsg)
133+
134+
ginkgo.By("Ignored namespace should not be recommended")
135+
ignoredVpa, err := vpaClientSet.AutoscalingV1().VerticalPodAutoscalers(ignoredNamespace).Get(context.TODO(), ignoredVpaCRD.Name, metav1.GetOptions{})
136+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
137+
gomega.Expect(ignoredVpa.Status.Conditions).Should(gomega.HaveLen(0))
138+
}

0 commit comments

Comments
 (0)