Change:
We need an E2E test for HPA behavior. HPA v2beta2 is graduating to stable! KEP: Tracking issue: Working doc: Slack channel: #sig-autoscaling-api . on #sig-autoscaling-api "Hey, I want to do this." (just so two people don't start the same work) a new E2E test that tests . There are currently two kinds of HPA tests in the Kubernetes E2E suite. One tests and the other tests . The feature is independent of the kind of metrics HPA is using. After HPA calculates scale for all metrics, it select the maximum and then applies user configured limits which are , and . Behavior specifies a scale up and down limit in rate (relative and absolute) as well as a delay. Defaults are: New behavior tests could be part of the CPU or the custom metrics suites, it doesn't really matter. Whichever is easier to implement and has the least flakes. Cases we should test: scale down works. Set , verify scale up, then verify scale down happens within 1 minute. up delay works. Set , continuously send high usage and verify scale up happens only after 5 minutes. autoscaling works. Set and . This will the replacement for the special 0 replicas case when we deprecate it, we should make sure it reliably works. Send a pull request (one squashed commit please) and notify #sig-autoscaling-api so we can take a look too. It will probably need API review. Feel free to reach out for help on #sig-autoscaling-api if you have any questions!
/sig autoscaling /triage accepted /help
This request has been marked as needing help from a contributor. Please ensure the request meets the requirements listed . If this request no longer meets these requirements, the label can be removed by commenting with the command. <detailsIn response to : /sig autoscaling /triage accepted /help Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/assign danishprakash
GitHub didn't allow me to assign the following users: danishprakash. Note that only , repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, issues/PRs can only have 10 assignees at the same time. For more information please see <detailsIn response to : /assign danishprakash Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/assign
Hey any update on this?
Yeah, I'll be sharing an update over the weekend.
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle rotten [1]:
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Reopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /close [1]:
Closing this issue. <detailsIn response to : The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closedReopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen are you planning to finish this task? Otherwise I'll take over.
You can't reopen an issue/PR unless you authored it or you are a collaborator. <detailsIn response to : /reopen Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen /assign pbetkier
Reopened this issue. <detailsIn response to : /reopen /assign pbetkier Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Reopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /close [1]:
Closing this issue. <detailsIn response to : The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closedReopen this issue or PR with Mark this issue or PR as fresh with Offer to help out with [Issue Triage][1] Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen
You can't reopen an issue/PR unless you authored it or you are a collaborator. <detailsIn response to : /reopen Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/remove-lifecycle rotten

Discussion:
import ( "time" <ins> autoscalingv2 "k8s.io/api/autoscaling/v2" </ins> "k8s.io/kubernetes/test/e2e/framework" e2eautoscaling "k8s.io/kubernetes/test/e2e/framework/autoscaling" admissionapi "k8s.io/pod-security-admission/api"

"github.com/onsi/ginkgo/v2" ) <del> var _ = SIGDescribe("[Feature:HPA] [Serial] [Slow] Horizontal pod autoscaling (non-default behavior)", func() { </del> <ins> var _ = SIGDescribe("[Feature:HPA] [Serial] [Slow] [Feature:BHPA] Horizontal pod autoscaling (non-default behavior)", func() { </ins> f := framework.NewDefaultFramework("horizontal-pod-autoscaling") f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged

framework.ExpectEqual(timeWaitedFor2 < deadline, true, "waited %s, wanted less than %s", timeWaitedFor2, deadline) }) }) <ins> ginkgo.Describe("with both scale up and down controls configured", func() { ginkgo.It("should keep recommendation within the range over two stabilization windows", func() { ginkgo.By("setting up resource consumer and HPA") initPods := 2 initCPUUsageTotal := initPods * usageForSingleReplica upScaleStabilization := 3 * time.Minute downScaleStabilization := 3 * time.Minute rc := e2eautoscaling.NewDynamicResourceConsumer( hpaName, f.Namespace.Name, e2eautoscaling.KindDeployment, initPods, initCPUUsageTotal, 0, 0, int64(podCPURequest), 200, f.ClientSet, f.ScalesGetter, e2eautoscaling.Disable, e2eautoscaling.Idle, ) defer rc.CleanUp() hpa := e2eautoscaling.CreateCPUHorizontalPodAutoscalerWithBehavior( rc, int32(targetCPUUtilizationPercent), 2, 10, e2eautoscaling.HPABehaviorWithStabilizationWindows(upScaleStabilization, downScaleStabilization), ) defer e2eautoscaling.DeleteHPAWithBehavior(rc, hpa.Name) ginkgo.By("triggering scale up by increasing consumption") rc.ConsumeCPU(5 * usageForSingleReplica) waitDeadline := upScaleStabilization ginkgo.By("verifying number of replicas stay in desired range within stabilisation window") rc.EnsureDesiredReplicasInRange(2, 2, waitDeadline, hpa.Name) ginkgo.By("waiting for replicas to scale up after stabilisation window passed") waitStart := time.Now() waitDeadline = maxHPAReactionTime + maxResourceConsumerDelay + waitBuffer rc.WaitForReplicas(4, waitDeadline) timeWaited := time.Now().Sub(waitStart) framework.Logf("time waited for scale up: %s", timeWaited) framework.ExpectEqual(timeWaited < waitDeadline, true, "waited %s, wanted less than %s", timeWaited, waitDeadline) ginkgo.By("triggering scale down by lowering consumption") rc.ConsumeCPU(2 * usageForSingleReplica) waitDeadline = downScaleStabilization ginkgo.By("verifying number of replicas stay in desired range within stabilisation window") rc.EnsureDesiredReplicasInRange(4, 4, waitDeadline, hpa.Name) ginkgo.By("waiting for replicas to scale down after stabilisation window passed") waitStart = time.Now() waitDeadline = maxHPAReactionTime + maxResourceConsumerDelay + waitBuffer rc.WaitForReplicas(2, waitDeadline) timeWaited = time.Now().Sub(waitStart) framework.Logf("time waited for scale down: %s", timeWaited) framework.ExpectEqual(timeWaited < waitDeadline, true, "waited %s, wanted less than %s", timeWaited, waitDeadline) }) ginkgo.It("should keep recommendation within the range with stabilization window and pod limit rate", func() { ginkgo.By("setting up resource consumer and HPA") initPods := 2 initCPUUsageTotal := initPods * usageForSingleReplica downScaleStabilization := 3 * time.Minute limitWindowLength := 2 * time.Minute podsLimitPerMinute := 1 rc := e2eautoscaling.NewDynamicResourceConsumer( hpaName, f.Namespace.Name, e2eautoscaling.KindDeployment, initPods, initCPUUsageTotal, 0, 0, int64(podCPURequest), 200, f.ClientSet, f.ScalesGetter, e2eautoscaling.Disable, e2eautoscaling.Idle, ) defer rc.CleanUp() scaleUpRule := e2eautoscaling.HPAScalingRuleWithScalingPolicy(autoscalingv2.PodsScalingPolicy, int32(podsLimitPerMinute), int32(limitWindowLength.Seconds())) scaleDownRule := e2eautoscaling.HPAScalingRuleWithStabilizationWindow(int32(downScaleStabilization.Seconds())) hpa := e2eautoscaling.CreateCPUHorizontalPodAutoscalerWithBehavior( rc, int32(targetCPUUtilizationPercent), 2, 10, e2eautoscaling.HPABehaviorWithScaleUpAndDownRules(scaleUpRule, scaleDownRule), ) defer e2eautoscaling.DeleteHPAWithBehavior(rc, hpa.Name) ginkgo.By("triggering scale up by increasing consumption") rc.ConsumeCPU(4 * usageForSingleReplica) waitDeadline := limitWindowLength ginkgo.By("verifying number of replicas stay in desired range with pod limit rate") rc.EnsureDesiredReplicasInRange(2, 3, waitDeadline, hpa.Name) ginkgo.By("waiting for replicas to scale up") waitStart := time.Now() waitDeadline = limitWindowLength + maxHPAReactionTime + maxResourceConsumerDelay + waitBuffer rc.WaitForReplicas(4, waitDeadline) timeWaited := time.Now().Sub(waitStart) framework.Logf("time waited for scale up: %s", timeWaited) framework.ExpectEqual(timeWaited < waitDeadline, true, "waited %s, wanted less than %s", timeWaited, waitDeadline) ginkgo.By("triggering scale down by lowering consumption") rc.ConsumeCPU(2 * usageForSingleReplica) ginkgo.By("verifying number of replicas stay in desired range within stabilisation window") waitDeadline = downScaleStabilization rc.EnsureDesiredReplicasInRange(4, 4, waitDeadline, hpa.Name) ginkgo.By("waiting for replicas to scale down after stabilisation window passed") waitStart = time.Now() waitDeadline = maxHPAReactionTime + maxResourceConsumerDelay + waitBuffer rc.WaitForReplicas(2, waitDeadline) timeWaited = time.Now().Sub(waitStart) framework.Logf("time waited for scale down: %s", timeWaited) framework.ExpectEqual(timeWaited < waitDeadline, true, "waited %s, wanted less than %s", timeWaited, waitDeadline) }) }) </ins> })