1
16
17 package autoscaling
18
19 import (
20 "context"
21 "time"
22
23 "github.com/onsi/ginkgo/v2"
24 "k8s.io/pod-security-admission/api"
25
26 autoscalingv2 "k8s.io/api/autoscaling/v2"
27 v1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/kubernetes/test/e2e/feature"
30 "k8s.io/kubernetes/test/e2e/framework"
31 e2eautoscaling "k8s.io/kubernetes/test/e2e/framework/autoscaling"
32 )
33
34 const (
35 titleUp = "Should scale from 1 pod to 3 pods and then from 3 pods to 5 pods"
36 titleDown = "Should scale from 5 pods to 3 pods and then from 3 pods to 1 pod"
37 titleAverageUtilization = " using Average Utilization for aggregation"
38 titleAverageValue = " using Average Value for aggregation"
39 valueMetricType = autoscalingv2.AverageValueMetricType
40 utilizationMetricType = autoscalingv2.UtilizationMetricType
41 cpuResource = v1.ResourceCPU
42 memResource = v1.ResourceMemory
43 )
44
45
46 var _ = SIGDescribe(feature.HPA, "Horizontal pod autoscaling (scale resource: CPU)", func() {
47 f := framework.NewDefaultFramework("horizontal-pod-autoscaling")
48 f.NamespacePodSecurityLevel = api.LevelBaseline
49
50 f.Describe(framework.WithSerial(), framework.WithSlow(), "Deployment (Pod Resource)", func() {
51 ginkgo.It(titleUp+titleAverageUtilization, func(ctx context.Context) {
52 scaleUp(ctx, "test-deployment", e2eautoscaling.KindDeployment, cpuResource, utilizationMetricType, false, f)
53 })
54 ginkgo.It(titleDown+titleAverageUtilization, func(ctx context.Context) {
55 scaleDown(ctx, "test-deployment", e2eautoscaling.KindDeployment, cpuResource, utilizationMetricType, false, f)
56 })
57 ginkgo.It(titleUp+titleAverageValue, func(ctx context.Context) {
58 scaleUp(ctx, "test-deployment", e2eautoscaling.KindDeployment, cpuResource, valueMetricType, false, f)
59 })
60 })
61
62 f.Describe(framework.WithSerial(), framework.WithSlow(), "Deployment (Container Resource)", func() {
63 ginkgo.It(titleUp+titleAverageUtilization, func(ctx context.Context) {
64 scaleUpContainerResource(ctx, "test-deployment", e2eautoscaling.KindDeployment, cpuResource, utilizationMetricType, f)
65 })
66 ginkgo.It(titleUp+titleAverageValue, func(ctx context.Context) {
67 scaleUpContainerResource(ctx, "test-deployment", e2eautoscaling.KindDeployment, cpuResource, valueMetricType, f)
68 })
69 })
70
71 f.Describe(framework.WithSerial(), framework.WithSlow(), "ReplicaSet", func() {
72 ginkgo.It(titleUp, func(ctx context.Context) {
73 scaleUp(ctx, "rs", e2eautoscaling.KindReplicaSet, cpuResource, utilizationMetricType, false, f)
74 })
75 ginkgo.It(titleDown, func(ctx context.Context) {
76 scaleDown(ctx, "rs", e2eautoscaling.KindReplicaSet, cpuResource, utilizationMetricType, false, f)
77 })
78 })
79
80
81 f.Describe(framework.WithSerial(), framework.WithSlow(), "ReplicationController", func() {
82 ginkgo.It(titleUp+" and verify decision stability", func(ctx context.Context) {
83 scaleUp(ctx, "rc", e2eautoscaling.KindRC, cpuResource, utilizationMetricType, true, f)
84 })
85 ginkgo.It(titleDown+" and verify decision stability", func(ctx context.Context) {
86 scaleDown(ctx, "rc", e2eautoscaling.KindRC, cpuResource, utilizationMetricType, true, f)
87 })
88 })
89
90 f.Describe("ReplicationController light", func() {
91 ginkgo.It("Should scale from 1 pod to 2 pods", func(ctx context.Context) {
92 st := &HPAScaleTest{
93 initPods: 1,
94 initCPUTotal: 150,
95 perPodCPURequest: 200,
96 targetValue: 50,
97 minPods: 1,
98 maxPods: 2,
99 firstScale: 2,
100 resourceType: cpuResource,
101 metricTargetType: utilizationMetricType,
102 }
103 st.run(ctx, "rc-light", e2eautoscaling.KindRC, f)
104 })
105 f.It(f.WithSlow(), "Should scale from 2 pods to 1 pod", func(ctx context.Context) {
106 st := &HPAScaleTest{
107 initPods: 2,
108 initCPUTotal: 50,
109 perPodCPURequest: 200,
110 targetValue: 50,
111 minPods: 1,
112 maxPods: 2,
113 firstScale: 1,
114 resourceType: cpuResource,
115 metricTargetType: utilizationMetricType,
116 }
117 st.run(ctx, "rc-light", e2eautoscaling.KindRC, f)
118 })
119 })
120
121 f.Describe(framework.WithSerial(), framework.WithSlow(), "ReplicaSet with idle sidecar (ContainerResource use case)", func() {
122
123 ginkgo.It(titleUp+" on a busy application with an idle sidecar container", func(ctx context.Context) {
124 scaleOnIdleSideCar(ctx, "rs", e2eautoscaling.KindReplicaSet, cpuResource, utilizationMetricType, false, f)
125 })
126
127
128 ginkgo.It("Should not scale up on a busy sidecar with an idle application", func(ctx context.Context) {
129 doNotScaleOnBusySidecar(ctx, "rs", e2eautoscaling.KindReplicaSet, cpuResource, utilizationMetricType, true, f)
130 })
131 })
132
133 f.Describe("CustomResourceDefinition", func() {
134 ginkgo.It("Should scale with a CRD targetRef", func(ctx context.Context) {
135 scaleTest := &HPAScaleTest{
136 initPods: 1,
137 initCPUTotal: 150,
138 perPodCPURequest: 200,
139 targetValue: 50,
140 minPods: 1,
141 maxPods: 2,
142 firstScale: 2,
143 resourceType: cpuResource,
144 metricTargetType: utilizationMetricType,
145 }
146 scaleTest.run(ctx, "foo-crd", e2eautoscaling.KindCRD, f)
147 })
148 })
149 })
150
151 var _ = SIGDescribe(feature.HPA, "Horizontal pod autoscaling (scale resource: Memory)", func() {
152 f := framework.NewDefaultFramework("horizontal-pod-autoscaling")
153 f.NamespacePodSecurityLevel = api.LevelBaseline
154
155 f.Describe(framework.WithSerial(), framework.WithSlow(), "Deployment (Pod Resource)", func() {
156 ginkgo.It(titleUp+titleAverageUtilization, func(ctx context.Context) {
157 scaleUp(ctx, "test-deployment", e2eautoscaling.KindDeployment, memResource, utilizationMetricType, false, f)
158 })
159 ginkgo.It(titleUp+titleAverageValue, func(ctx context.Context) {
160 scaleUp(ctx, "test-deployment", e2eautoscaling.KindDeployment, memResource, valueMetricType, false, f)
161 })
162 })
163
164 f.Describe(framework.WithSerial(), framework.WithSlow(), "Deployment (Container Resource)", func() {
165 ginkgo.It(titleUp+titleAverageUtilization, func(ctx context.Context) {
166 scaleUpContainerResource(ctx, "test-deployment", e2eautoscaling.KindDeployment, memResource, utilizationMetricType, f)
167 })
168 ginkgo.It(titleUp+titleAverageValue, func(ctx context.Context) {
169 scaleUpContainerResource(ctx, "test-deployment", e2eautoscaling.KindDeployment, memResource, valueMetricType, f)
170 })
171 })
172 })
173
174
175 type HPAScaleTest struct {
176 initPods int
177 initCPUTotal int
178 initMemTotal int
179 perPodCPURequest int64
180 perPodMemRequest int64
181 targetValue int32
182 minPods int32
183 maxPods int32
184 firstScale int
185 firstScaleStasis time.Duration
186 cpuBurst int
187 memBurst int
188 secondScale int32
189 resourceType v1.ResourceName
190 metricTargetType autoscalingv2.MetricTargetType
191 }
192
193
194
195
196
197
198 func (st *HPAScaleTest) run(ctx context.Context, name string, kind schema.GroupVersionKind, f *framework.Framework) {
199 const timeToWait = 15 * time.Minute
200 initCPUTotal, initMemTotal := 0, 0
201 if st.resourceType == cpuResource {
202 initCPUTotal = st.initCPUTotal
203 } else if st.resourceType == memResource {
204 initMemTotal = st.initMemTotal
205 }
206 rc := e2eautoscaling.NewDynamicResourceConsumer(ctx, name, f.Namespace.Name, kind, st.initPods, initCPUTotal, initMemTotal, 0, st.perPodCPURequest, st.perPodMemRequest, f.ClientSet, f.ScalesGetter, e2eautoscaling.Disable, e2eautoscaling.Idle)
207 ginkgo.DeferCleanup(rc.CleanUp)
208 hpa := e2eautoscaling.CreateResourceHorizontalPodAutoscaler(ctx, rc, st.resourceType, st.metricTargetType, st.targetValue, st.minPods, st.maxPods)
209 ginkgo.DeferCleanup(e2eautoscaling.DeleteHorizontalPodAutoscaler, rc, hpa.Name)
210
211 rc.WaitForReplicas(ctx, st.firstScale, timeToWait)
212 if st.firstScaleStasis > 0 {
213 rc.EnsureDesiredReplicasInRange(ctx, st.firstScale, st.firstScale+1, st.firstScaleStasis, hpa.Name)
214 }
215 if st.resourceType == cpuResource && st.cpuBurst > 0 && st.secondScale > 0 {
216 rc.ConsumeCPU(st.cpuBurst)
217 rc.WaitForReplicas(ctx, int(st.secondScale), timeToWait)
218 }
219 if st.resourceType == memResource && st.memBurst > 0 && st.secondScale > 0 {
220 rc.ConsumeMem(st.memBurst)
221 rc.WaitForReplicas(ctx, int(st.secondScale), timeToWait)
222 }
223 }
224
225 func scaleUp(ctx context.Context, name string, kind schema.GroupVersionKind, resourceType v1.ResourceName, metricTargetType autoscalingv2.MetricTargetType, checkStability bool, f *framework.Framework) {
226 stasis := 0 * time.Minute
227 if checkStability {
228 stasis = 10 * time.Minute
229 }
230 st := &HPAScaleTest{
231 initPods: 1,
232 perPodCPURequest: 500,
233 perPodMemRequest: 500,
234 targetValue: getTargetValueByType(100, 20, metricTargetType),
235 minPods: 1,
236 maxPods: 5,
237 firstScale: 3,
238 firstScaleStasis: stasis,
239 secondScale: 5,
240 resourceType: resourceType,
241 metricTargetType: metricTargetType,
242 }
243 if resourceType == cpuResource {
244 st.initCPUTotal = 250
245 st.cpuBurst = 700
246 }
247 if resourceType == memResource {
248 st.initMemTotal = 250
249 st.memBurst = 700
250 }
251 st.run(ctx, name, kind, f)
252 }
253
254 func scaleDown(ctx context.Context, name string, kind schema.GroupVersionKind, resourceType v1.ResourceName, metricTargetType autoscalingv2.MetricTargetType, checkStability bool, f *framework.Framework) {
255 stasis := 0 * time.Minute
256 if checkStability {
257 stasis = 10 * time.Minute
258 }
259 st := &HPAScaleTest{
260 initPods: 5,
261 perPodCPURequest: 500,
262 perPodMemRequest: 500,
263 targetValue: getTargetValueByType(150, 30, metricTargetType),
264 minPods: 1,
265 maxPods: 5,
266 firstScale: 3,
267 firstScaleStasis: stasis,
268 cpuBurst: 10,
269 secondScale: 1,
270 resourceType: resourceType,
271 metricTargetType: metricTargetType,
272 }
273 if resourceType == cpuResource {
274 st.initCPUTotal = 325
275 st.cpuBurst = 10
276 }
277 if resourceType == memResource {
278 st.initMemTotal = 325
279 st.memBurst = 10
280 }
281 st.run(ctx, name, kind, f)
282 }
283
284 type HPAContainerResourceScaleTest struct {
285 initPods int
286 initCPUTotal int
287 initMemTotal int
288 perContainerCPURequest int64
289 perContainerMemRequest int64
290 targetValue int32
291 minPods int32
292 maxPods int32
293 noScale bool
294 noScaleStasis time.Duration
295 firstScale int
296 firstScaleStasis time.Duration
297 cpuBurst int
298 memBurst int
299 secondScale int32
300 sidecarStatus e2eautoscaling.SidecarStatusType
301 sidecarType e2eautoscaling.SidecarWorkloadType
302 resourceType v1.ResourceName
303 metricTargetType autoscalingv2.MetricTargetType
304 }
305
306 func (st *HPAContainerResourceScaleTest) run(ctx context.Context, name string, kind schema.GroupVersionKind, f *framework.Framework) {
307 const timeToWait = 15 * time.Minute
308 initCPUTotal, initMemTotal := 0, 0
309 if st.resourceType == cpuResource {
310 initCPUTotal = st.initCPUTotal
311 } else if st.resourceType == memResource {
312 initMemTotal = st.initMemTotal
313 }
314 rc := e2eautoscaling.NewDynamicResourceConsumer(ctx, name, f.Namespace.Name, kind, st.initPods, initCPUTotal, initMemTotal, 0, st.perContainerCPURequest, st.perContainerMemRequest, f.ClientSet, f.ScalesGetter, st.sidecarStatus, st.sidecarType)
315 ginkgo.DeferCleanup(rc.CleanUp)
316 hpa := e2eautoscaling.CreateContainerResourceHorizontalPodAutoscaler(ctx, rc, st.resourceType, st.metricTargetType, st.targetValue, st.minPods, st.maxPods)
317 ginkgo.DeferCleanup(e2eautoscaling.DeleteContainerResourceHPA, rc, hpa.Name)
318
319 if st.noScale {
320 if st.noScaleStasis > 0 {
321 rc.EnsureDesiredReplicasInRange(ctx, st.initPods, st.initPods, st.noScaleStasis, hpa.Name)
322 }
323 } else {
324 rc.WaitForReplicas(ctx, st.firstScale, timeToWait)
325 if st.firstScaleStasis > 0 {
326 rc.EnsureDesiredReplicasInRange(ctx, st.firstScale, st.firstScale+1, st.firstScaleStasis, hpa.Name)
327 }
328 if st.resourceType == cpuResource && st.cpuBurst > 0 && st.secondScale > 0 {
329 rc.ConsumeCPU(st.cpuBurst)
330 rc.WaitForReplicas(ctx, int(st.secondScale), timeToWait)
331 }
332 if st.resourceType == memResource && st.memBurst > 0 && st.secondScale > 0 {
333 rc.ConsumeMem(st.memBurst)
334 rc.WaitForReplicas(ctx, int(st.secondScale), timeToWait)
335 }
336 }
337 }
338
339 func scaleUpContainerResource(ctx context.Context, name string, kind schema.GroupVersionKind, resourceType v1.ResourceName, metricTargetType autoscalingv2.MetricTargetType, f *framework.Framework) {
340 st := &HPAContainerResourceScaleTest{
341 initPods: 1,
342 perContainerCPURequest: 500,
343 perContainerMemRequest: 500,
344 targetValue: getTargetValueByType(100, 20, metricTargetType),
345 minPods: 1,
346 maxPods: 5,
347 firstScale: 3,
348 firstScaleStasis: 0,
349 secondScale: 5,
350 resourceType: resourceType,
351 metricTargetType: metricTargetType,
352 sidecarStatus: e2eautoscaling.Disable,
353 sidecarType: e2eautoscaling.Idle,
354 }
355 if resourceType == cpuResource {
356 st.initCPUTotal = 250
357 st.cpuBurst = 700
358 }
359 if resourceType == memResource {
360 st.initMemTotal = 250
361 st.memBurst = 700
362 }
363 st.run(ctx, name, kind, f)
364 }
365
366 func scaleOnIdleSideCar(ctx context.Context, name string, kind schema.GroupVersionKind, resourceType v1.ResourceName, metricTargetType autoscalingv2.MetricTargetType, checkStability bool, f *framework.Framework) {
367
368 stasis := 0 * time.Minute
369 if checkStability {
370 stasis = 10 * time.Minute
371 }
372 st := &HPAContainerResourceScaleTest{
373 initPods: 1,
374 initCPUTotal: 125,
375 perContainerCPURequest: 250,
376 targetValue: 20,
377 minPods: 1,
378 maxPods: 5,
379 firstScale: 3,
380 firstScaleStasis: stasis,
381 cpuBurst: 500,
382 secondScale: 5,
383 resourceType: resourceType,
384 metricTargetType: metricTargetType,
385 sidecarStatus: e2eautoscaling.Enable,
386 sidecarType: e2eautoscaling.Idle,
387 }
388 st.run(ctx, name, kind, f)
389 }
390
391 func doNotScaleOnBusySidecar(ctx context.Context, name string, kind schema.GroupVersionKind, resourceType v1.ResourceName, metricTargetType autoscalingv2.MetricTargetType, checkStability bool, f *framework.Framework) {
392
393 stasis := 0 * time.Minute
394 if checkStability {
395 stasis = 1 * time.Minute
396 }
397 st := &HPAContainerResourceScaleTest{
398 initPods: 1,
399 initCPUTotal: 250,
400 perContainerCPURequest: 500,
401 targetValue: 20,
402 minPods: 1,
403 maxPods: 5,
404 cpuBurst: 700,
405 sidecarStatus: e2eautoscaling.Enable,
406 sidecarType: e2eautoscaling.Busy,
407 resourceType: resourceType,
408 metricTargetType: metricTargetType,
409 noScale: true,
410 noScaleStasis: stasis,
411 }
412 st.run(ctx, name, kind, f)
413 }
414
415 func getTargetValueByType(averageValueTarget, averageUtilizationTarget int, targetType autoscalingv2.MetricTargetType) int32 {
416 if targetType == utilizationMetricType {
417 return int32(averageUtilizationTarget)
418 }
419 return int32(averageValueTarget)
420 }
421
View as plain text