From 141eaf79eed58481a51569caf0df7deba016f111 Mon Sep 17 00:00:00 2001 From: Ivan Glushkov Date: Mon, 14 Oct 2019 19:13:17 +0400 Subject: [PATCH 1/5] Introduces all API changes needed for Configurable HPA PR --- api/api-rules/violation_exceptions.list | 1 + pkg/apis/autoscaling/annotations.go | 4 + pkg/apis/autoscaling/types.go | 84 +++++++++++++++++++ pkg/apis/autoscaling/v1/conversion.go | 21 ++++- pkg/apis/autoscaling/v2beta1/conversion.go | 44 ++++++++++ .../k8s.io/api/autoscaling/v2beta2/types.go | 84 +++++++++++++++++++ 6 files changed, 236 insertions(+), 2 deletions(-) diff --git a/api/api-rules/violation_exceptions.list b/api/api-rules/violation_exceptions.list index 3ef747ae5c8..ac6de8f5fa7 100644 --- a/api/api-rules/violation_exceptions.list +++ b/api/api-rules/violation_exceptions.list @@ -83,6 +83,7 @@ API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalP API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerSpec,Metrics API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerStatus,Conditions API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerStatus,CurrentMetrics +API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HPAScalingRules,Policies API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerList,Items API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerSpec,Metrics API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerStatus,Conditions diff --git a/pkg/apis/autoscaling/annotations.go b/pkg/apis/autoscaling/annotations.go index ccf0345353f..e49aaab2eca 100644 --- a/pkg/apis/autoscaling/annotations.go +++ b/pkg/apis/autoscaling/annotations.go @@ -32,3 +32,7 @@ const HorizontalPodAutoscalerConditionsAnnotation = "autoscaling.alpha.kubernete // metrics are present. This is here because it's used by both the v2beta1 defaulting // logic, and the pseudo-defaulting done in v1 conversion. const DefaultCPUUtilization = 80 + +// BehaviorSpecsAnnotation is the annotation which holds the HPA constraints specs +// when converting the `Behavior` field from autoscaling/v2beta2 +const BehaviorSpecsAnnotation = "autoscaling.alpha.kubernetes.io/behavior" diff --git a/pkg/apis/autoscaling/types.go b/pkg/apis/autoscaling/types.go index 43f42d64ec9..e2f0f4966db 100644 --- a/pkg/apis/autoscaling/types.go +++ b/pkg/apis/autoscaling/types.go @@ -95,6 +95,90 @@ type HorizontalPodAutoscalerSpec struct { // more information about how each type of metric must respond. // +optional Metrics []MetricSpec + + // behavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // +optional + Behavior *HorizontalPodAutoscalerBehavior +} + +// HorizontalPodAutoscalerBehavior configures a scaling behavior for Up and Down direction +// (scaleUp and scaleDown fields respectively). +type HorizontalPodAutoscalerBehavior struct { + // scaleUp is scaling policy for scaling Up. + // If not set, the default value is the higher of: + // * increase no more than 4 pods per 60 seconds + // * double the number of pods per 60 seconds + // No stabilization is used. + // +optional + ScaleUp *HPAScalingRules + // scaleDown is scaling policy for scaling Down. + // If not set, the default value is to allow to scale down to minReplicas pods, with a + // 300 second stabilization window (i.e., the highest recommendation for + // the last 300sec is used). + // +optional + ScaleDown *HPAScalingRules +} + +// ScalingPolicySelect is used to specify which policy should be used while scaling in a certain direction +type ScalingPolicySelect string + +const ( + // MaxPolicySelect selects the policy with the highest possible change. + MaxPolicySelect ScalingPolicySelect = "Max" + // MinPolicySelect selects the policy with the lowest possible change. + MinPolicySelect ScalingPolicySelect = "Min" + // DisabledPolicySelect disables the scaling in this direction. + DisabledPolicySelect ScalingPolicySelect = "Disabled" +) + +// HPAScalingRules configures the scaling behavior for one direction. +// These Rules are applied after calculating DesiredReplicas from metrics for the HPA. +// They can limit the scaling velocity by specifying scaling policies. +// They can prevent flapping by specifying the stabilization window, so that the +// number of replicas is not set instantly, instead, the safest value from the stabilization +// window is chosen. +type HPAScalingRules struct { + // StabilizationWindowSeconds is the number of seconds for which past recommendations should be + // considered while scaling up or scaling down. + // StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). + // If not set, use the default values: + // - For scale up: 0 (i.e. no stabilization is done). + // - For scale down: 300 (i.e. the stabilization window is 300 seconds long). + // +optional + StabilizationWindowSeconds *int32 + // selectPolicy is used to specify which policy should be used. + // If not set, the default value MaxPolicySelect is used. + // +optional + SelectPolicy *ScalingPolicySelect + // policies is a list of potential scaling polices which can used during scaling. + // At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid + // +optional + Policies []HPAScalingPolicy +} + +// HPAScalingPolicyType is the type of the policy which could be used while making scaling decisions. +type HPAScalingPolicyType string + +const ( + // PodsScalingPolicy is a policy used to specify a change in absolute number of pods. + PodsScalingPolicy HPAScalingPolicyType = "Pods" + // PercentScalingPolicy is a policy used to specify a relative amount of change with respect to + // the current number of pods. + PercentScalingPolicy HPAScalingPolicyType = "Percent" +) + +// HPAScalingPolicy is a single policy which must hold true for a specified past interval. +type HPAScalingPolicy struct { + // Type is used to specify the scaling policy. + Type HPAScalingPolicyType + // Value contains the amount of change which is permitted by the policy. + // It must be greater than zero + Value int32 + // PeriodSeconds specifies the window of time for which the policy should hold true. + // PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min). + PeriodSeconds int32 } // MetricSourceType indicates the type of metric. diff --git a/pkg/apis/autoscaling/v1/conversion.go b/pkg/apis/autoscaling/v1/conversion.go index 8aca1c12522..0d9f4c59382 100644 --- a/pkg/apis/autoscaling/v1/conversion.go +++ b/pkg/apis/autoscaling/v1/conversion.go @@ -289,9 +289,9 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i } } - if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 || len(currentConditions) > 0 { + if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 || len(currentConditions) > 0 || in.Spec.Behavior != nil { old := out.Annotations - out.Annotations = make(map[string]string, len(old)+3) + out.Annotations = make(map[string]string, len(old)+4) for k, v := range old { out.Annotations[k] = v } @@ -313,6 +313,14 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i out.Annotations[autoscaling.MetricStatusesAnnotation] = string(currentMetricsEnc) } + if in.Spec.Behavior != nil { + behaviorEnc, err := json.Marshal(in.Spec.Behavior) + if err != nil { + return err + } + out.Annotations[autoscaling.BehaviorSpecsAnnotation] = string(behaviorEnc) + } + if len(in.Status.Conditions) > 0 { currentConditionsEnc, err := json.Marshal(currentConditions) if err != nil { @@ -349,6 +357,15 @@ func Convert_v1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(i delete(out.Annotations, autoscaling.MetricSpecsAnnotation) } + if behaviorEnc, hasConstraints := out.Annotations[autoscaling.BehaviorSpecsAnnotation]; hasConstraints { + var behavior autoscaling.HorizontalPodAutoscalerBehavior + if err := json.Unmarshal([]byte(behaviorEnc), &behavior); err != nil { + return err + } + out.Spec.Behavior = &behavior + delete(out.Annotations, autoscaling.BehaviorSpecsAnnotation) + } + if currentMetricsEnc, hasCurrentMetrics := out.Annotations[autoscaling.MetricStatusesAnnotation]; hasCurrentMetrics { // ignore any existing status values -- the ones here have more information var currentMetrics []autoscalingv1.MetricStatus diff --git a/pkg/apis/autoscaling/v2beta1/conversion.go b/pkg/apis/autoscaling/v2beta1/conversion.go index dc03dfe4281..2fb8c1856a2 100644 --- a/pkg/apis/autoscaling/v2beta1/conversion.go +++ b/pkg/apis/autoscaling/v2beta1/conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v2beta1 import ( + "encoding/json" + autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1" v1 "k8s.io/api/core/v1" @@ -257,3 +259,45 @@ func Convert_v2beta1_PodsMetricStatus_To_autoscaling_PodsMetricStatus(in *autosc } return nil } + +func Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in *autoscaling.HorizontalPodAutoscaler, out *autoscalingv2beta1.HorizontalPodAutoscaler, s conversion.Scope) error { + if err := autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in, out, s); err != nil { + return err + } + if in.Spec.Behavior != nil { + old := out.Annotations + out.Annotations = make(map[string]string, len(old)+1) + for k, v := range old { + out.Annotations[k] = v + } + + behaviorEnc, err := json.Marshal(in.Spec.Behavior) + if err != nil { + return err + } + // Even if the annotation for behavior exists, we will just overwrite it + out.Annotations[autoscaling.BehaviorSpecsAnnotation] = string(behaviorEnc) + } + + return nil +} + +func Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in *autoscalingv2beta1.HorizontalPodAutoscaler, out *autoscaling.HorizontalPodAutoscaler, s conversion.Scope) error { + if err := autoConvert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in, out, s); err != nil { + return err + } + + if behaviorEnc, hasBehaviors := out.Annotations[autoscaling.BehaviorSpecsAnnotation]; hasBehaviors { + var behavior autoscaling.HorizontalPodAutoscalerBehavior + if err := json.Unmarshal([]byte(behaviorEnc), &behavior); err != nil { + return err + } + out.Spec.Behavior = &behavior + delete(out.Annotations, autoscaling.BehaviorSpecsAnnotation) + } + return nil +} + +func Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in *autoscaling.HorizontalPodAutoscalerSpec, out *autoscalingv2beta1.HorizontalPodAutoscalerSpec, s conversion.Scope) error { + return autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in, out, s) +} diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/types.go b/staging/src/k8s.io/api/autoscaling/v2beta2/types.go index 4480c7da8d2..614caeb6c52 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/types.go +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/types.go @@ -72,6 +72,12 @@ type HorizontalPodAutoscalerSpec struct { // If not set, the default metric will be set to 80% average CPU utilization. // +optional Metrics []MetricSpec `json:"metrics,omitempty" protobuf:"bytes,4,rep,name=metrics"` + + // behavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // +optional + Behavior *HorizontalPodAutoscalerBehavior `json:"behavior,omitempty" protobuf:"bytes,5,opt,name=behavior"` } // CrossVersionObjectReference contains enough information to let you identify the referred resource. @@ -117,6 +123,84 @@ type MetricSpec struct { External *ExternalMetricSource `json:"external,omitempty" protobuf:"bytes,5,opt,name=external"` } +// HorizontalPodAutoscalerBehavior configures the scaling behavior of the target +// in both Up and Down directions (scaleUp and scaleDown fields respectively). +type HorizontalPodAutoscalerBehavior struct { + // scaleUp is scaling policy for scaling Up. + // If not set, the default value is the higher of: + // * increase no more than 4 pods per 60 seconds + // * double the number of pods per 60 seconds + // No stabilization is used. + // +optional + ScaleUp *HPAScalingRules `json:"scaleUp,omitempty" protobuf:"bytes,1,opt,name=scaleUp"` + // scaleDown is scaling policy for scaling Down. + // If not set, the default value is to allow to scale down to minReplicas pods, with a + // 300 second stabilization window (i.e., the highest recommendation for + // the last 300sec is used). + // +optional + ScaleDown *HPAScalingRules `json:"scaleDown,omitempty" protobuf:"bytes,2,opt,name=scaleDown"` +} + +// ScalingPolicySelect is used to specify which policy should be used while scaling in a certain direction +type ScalingPolicySelect string + +const ( + // MaxPolicySelect selects the policy with the highest possible change. + MaxPolicySelect ScalingPolicySelect = "Max" + // MinPolicySelect selects the policy with the lowest possible change. + MinPolicySelect ScalingPolicySelect = "Min" + // DisabledPolicySelect disables the scaling in this direction. + DisabledPolicySelect ScalingPolicySelect = "Disabled" +) + +// HPAScalingRules configures the scaling behavior for one direction. +// These Rules are applied after calculating DesiredReplicas from metrics for the HPA. +// They can limit the scaling velocity by specifying scaling policies. +// They can prevent flapping by specifying the stabilization window, so that the +// number of replicas is not set instantly, instead, the safest value from the stabilization +// window is chosen. +type HPAScalingRules struct { + // StabilizationWindowSeconds is the number of seconds for which past recommendations should be + // considered while scaling up or scaling down. + // StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). + // If not set, use the default values: + // - For scale up: 0 (i.e. no stabilization is done). + // - For scale down: 300 (i.e. the stabilization window is 300 seconds long). + // +optional + StabilizationWindowSeconds *int32 `json:"stabilizationWindowSeconds" protobuf:"varint,3,opt,name=stabilizationWindowSeconds"` + // selectPolicy is used to specify which policy should be used. + // If not set, the default value MaxPolicySelect is used. + // +optional + SelectPolicy *ScalingPolicySelect `json:"selectPolicy,omitempty" protobuf:"bytes,1,opt,name=selectPolicy"` + // policies is a list of potential scaling polices which can be used during scaling. + // At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid + // +optional + Policies []HPAScalingPolicy `json:"policies,omitempty" protobuf:"bytes,2,rep,name=policies"` +} + +// HPAScalingPolicyType is the type of the policy which could be used while making scaling decisions. +type HPAScalingPolicyType string + +const ( + // PodsScalingPolicy is a policy used to specify a change in absolute number of pods. + PodsScalingPolicy HPAScalingPolicyType = "Pods" + // PercentScalingPolicy is a policy used to specify a relative amount of change with respect to + // the current number of pods. + PercentScalingPolicy HPAScalingPolicyType = "Percent" +) + +// HPAScalingPolicy is a single policy which must hold true for a specified past interval. +type HPAScalingPolicy struct { + // Type is used to specify the scaling policy. + Type HPAScalingPolicyType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=HPAScalingPolicyType"` + // Value contains the amount of change which is permitted by the policy. + // It must be greater than zero + Value int32 `json:"value" protobuf:"varint,2,opt,name=value"` + // PeriodSeconds specifies the window of time for which the policy should hold true. + // PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min). + PeriodSeconds int32 `json:"periodSeconds" protobuf:"varint,3,opt,name=periodSeconds"` +} + // MetricSourceType indicates the type of metric. type MetricSourceType string From 5c70cda6e51b3b732c707bc937c1c8c9787ccc39 Mon Sep 17 00:00:00 2001 From: Ivan Glushkov Date: Tue, 10 Dec 2019 19:48:46 +0400 Subject: [PATCH 2/5] Adds validation rules and proper defaults --- pkg/apis/autoscaling/v2beta2/defaults.go | 85 ++++++++++++++++++- pkg/apis/autoscaling/validation/validation.go | 74 ++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) diff --git a/pkg/apis/autoscaling/v2beta2/defaults.go b/pkg/apis/autoscaling/v2beta2/defaults.go index a3a061ce259..a688bfa6070 100644 --- a/pkg/apis/autoscaling/v2beta2/defaults.go +++ b/pkg/apis/autoscaling/v2beta2/defaults.go @@ -18,11 +18,52 @@ package v2beta2 import ( autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/apis/autoscaling" ) +var ( + // These constants repeats previous HPA behavior + scaleUpLimitPercent int32 = 100 + scaleUpLimitMinimumPods int32 = 4 + scaleUpPeriod int32 = 15 + scaleUpStabilizationSeconds int32 + maxPolicy = autoscalingv2beta2.MaxPolicySelect + defaultHPAScaleUpRules = autoscalingv2beta2.HPAScalingRules{ + StabilizationWindowSeconds: &scaleUpStabilizationSeconds, + SelectPolicy: &maxPolicy, + Policies: []autoscalingv2beta2.HPAScalingPolicy{ + { + Type: autoscalingv2beta2.PodsScalingPolicy, + Value: scaleUpLimitMinimumPods, + PeriodSeconds: scaleUpPeriod, + }, + { + Type: autoscalingv2beta2.PercentScalingPolicy, + Value: scaleUpLimitPercent, + PeriodSeconds: scaleUpPeriod, + }, + }, + } + scaleDownPeriod int32 = 15 + // Currently we can set the downscaleStabilizationWindow from the command line + // So we can not rewrite the command line option from here + scaleDownStabilizationSeconds *int32 = nil + scaleDownLimitPercent int32 = 100 + defaultHPAScaleDownRules = autoscalingv2beta2.HPAScalingRules{ + StabilizationWindowSeconds: scaleDownStabilizationSeconds, + SelectPolicy: &maxPolicy, + Policies: []autoscalingv2beta2.HPAScalingPolicy{ + { + Type: autoscalingv2beta2.PercentScalingPolicy, + Value: scaleDownLimitPercent, + PeriodSeconds: scaleDownPeriod, + }, + }, + } +) + func addDefaultingFuncs(scheme *runtime.Scheme) error { return RegisterDefaults(scheme) } @@ -48,4 +89,46 @@ func SetDefaults_HorizontalPodAutoscaler(obj *autoscalingv2beta2.HorizontalPodAu }, } } + SetDefaults_HorizontalPodAutoscalerBehavior(obj) +} + +// SetDefaults_HorizontalPodAutoscalerBehavior fills the behavior if it is not null +func SetDefaults_HorizontalPodAutoscalerBehavior(obj *autoscalingv2beta2.HorizontalPodAutoscaler) { + // if behavior is specified, we should fill all the 'nil' values with the default ones + if obj.Spec.Behavior != nil { + obj.Spec.Behavior.ScaleUp = GenerateHPAScaleUpRules(obj.Spec.Behavior.ScaleUp) + obj.Spec.Behavior.ScaleDown = GenerateHPAScaleDownRules(obj.Spec.Behavior.ScaleDown) + } +} + +// GenerateHPAScaleUpRules returns a fully-initialized HPAScalingRules value +// We guarantee that no pointer in the structure will have the 'nil' value +func GenerateHPAScaleUpRules(scalingRules *autoscalingv2beta2.HPAScalingRules) *autoscalingv2beta2.HPAScalingRules { + defaultScalingRules := defaultHPAScaleUpRules.DeepCopy() + return copyHPAScalingRules(scalingRules, defaultScalingRules) +} + +// GenerateHPAScaleDownRules returns a fully-initialized HPAScalingRules value +// We guarantee that no pointer in the structure will have the 'nil' value +// EXCEPT StabilizationWindowSeconds, for reasoning check the comment for defaultHPAScaleDownRules +func GenerateHPAScaleDownRules(scalingRules *autoscalingv2beta2.HPAScalingRules) *autoscalingv2beta2.HPAScalingRules { + defaultScalingRules := defaultHPAScaleDownRules.DeepCopy() + return copyHPAScalingRules(scalingRules, defaultScalingRules) +} + +// copyHPAScalingRules copies all non-`nil` fields in HPA constraint structure +func copyHPAScalingRules(from, to *autoscalingv2beta2.HPAScalingRules) *autoscalingv2beta2.HPAScalingRules { + if from == nil { + return to + } + if from.SelectPolicy != nil { + to.SelectPolicy = from.SelectPolicy + } + if from.StabilizationWindowSeconds != nil { + to.StabilizationWindowSeconds = from.StabilizationWindowSeconds + } + if from.Policies != nil { + to.Policies = from.Policies + } + return to } diff --git a/pkg/apis/autoscaling/validation/validation.go b/pkg/apis/autoscaling/validation/validation.go index 97686ea410f..8e5b457b20f 100644 --- a/pkg/apis/autoscaling/validation/validation.go +++ b/pkg/apis/autoscaling/validation/validation.go @@ -30,6 +30,13 @@ import ( "k8s.io/kubernetes/pkg/features" ) +const ( + // MaxPeriodSeconds is the largest allowed scaling policy period (in seconds) + MaxPeriodSeconds int32 = 1800 + // MaxStabilizationWindowSeconds is the largest allowed stabilization window (in seconds) + MaxStabilizationWindowSeconds int32 = 3600 +) + // ValidateScale validates a Scale and returns an ErrorList with any errors. func ValidateScale(scale *autoscaling.Scale) field.ErrorList { allErrs := field.ErrorList{} @@ -65,6 +72,9 @@ func validateHorizontalPodAutoscalerSpec(autoscaler autoscaling.HorizontalPodAut if refErrs := validateMetrics(autoscaler.Metrics, fldPath.Child("metrics"), autoscaler.MinReplicas); len(refErrs) > 0 { allErrs = append(allErrs, refErrs...) } + if refErrs := validateBehavior(autoscaler.Behavior, fldPath.Child("behavior")); len(refErrs) > 0 { + allErrs = append(allErrs, refErrs...) + } return allErrs } @@ -165,6 +175,70 @@ func validateMetrics(metrics []autoscaling.MetricSpec, fldPath *field.Path, minR return allErrs } +func validateBehavior(behavior *autoscaling.HorizontalPodAutoscalerBehavior, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if behavior != nil { + if scaleUpErrs := validateScalingRules(behavior.ScaleUp, fldPath.Child("scaleUp")); len(scaleUpErrs) > 0 { + allErrs = append(allErrs, scaleUpErrs...) + } + if scaleDownErrs := validateScalingRules(behavior.ScaleDown, fldPath.Child("scaleDown")); len(scaleDownErrs) > 0 { + allErrs = append(allErrs, scaleDownErrs...) + } + } + return allErrs +} + +var validSelectPolicyTypes = sets.NewString(string(autoscaling.MaxPolicySelect), string(autoscaling.MinPolicySelect), string(autoscaling.DisabledPolicySelect)) +var validSelectPolicyTypesList = validSelectPolicyTypes.List() + +func validateScalingRules(rules *autoscaling.HPAScalingRules, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if rules != nil { + if rules.StabilizationWindowSeconds != nil && *rules.StabilizationWindowSeconds < 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("stabilizationWindowSeconds"), rules.StabilizationWindowSeconds, "must be greater than or equal to zero")) + } + if rules.StabilizationWindowSeconds != nil && *rules.StabilizationWindowSeconds > MaxStabilizationWindowSeconds { + allErrs = append(allErrs, field.Invalid(fldPath.Child("stabilizationWindowSeconds"), rules.StabilizationWindowSeconds, + fmt.Sprintf("must be less than or equal to %v", MaxStabilizationWindowSeconds))) + } + if rules.SelectPolicy != nil && !validSelectPolicyTypes.Has(string(*rules.SelectPolicy)) { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("selectPolicy"), rules.SelectPolicy, validSelectPolicyTypesList)) + } + policiesPath := fldPath.Child("policies") + if len(rules.Policies) == 0 { + allErrs = append(allErrs, field.Required(policiesPath, "must specify at least one Policy")) + } + for i, policy := range rules.Policies { + idxPath := policiesPath.Index(i) + if policyErrs := validateScalingPolicy(policy, idxPath); len(policyErrs) > 0 { + allErrs = append(allErrs, policyErrs...) + } + } + } + return allErrs +} + +var validPolicyTypes = sets.NewString(string(autoscaling.PodsScalingPolicy), string(autoscaling.PercentScalingPolicy)) +var validPolicyTypesList = validPolicyTypes.List() + +func validateScalingPolicy(policy autoscaling.HPAScalingPolicy, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if policy.Type != autoscaling.PodsScalingPolicy && policy.Type != autoscaling.PercentScalingPolicy { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), policy.Type, validPolicyTypesList)) + } + if policy.Value <= 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("value"), policy.Value, "must be greater than zero")) + } + if policy.PeriodSeconds <= 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("periodSeconds"), policy.PeriodSeconds, "must be greater than zero")) + } + if policy.PeriodSeconds > MaxPeriodSeconds { + allErrs = append(allErrs, field.Invalid(fldPath.Child("periodSeconds"), policy.PeriodSeconds, + fmt.Sprintf("must be less than or equal to %v", MaxPeriodSeconds))) + } + return allErrs +} + var validMetricSourceTypes = sets.NewString(string(autoscaling.ObjectMetricSourceType), string(autoscaling.PodsMetricSourceType), string(autoscaling.ResourceMetricSourceType), string(autoscaling.ExternalMetricSourceType)) var validMetricSourceTypesList = validMetricSourceTypes.List() From 27ffe439b61aaee196190749f1191d9957c86ca1 Mon Sep 17 00:00:00 2001 From: Ivan Glushkov Date: Mon, 14 Oct 2019 19:33:09 +0400 Subject: [PATCH 3/5] Adds the algorithm implementation for the Configurable HPA --- pkg/apis/autoscaling/v2beta2/doc.go | 1 + pkg/controller/podautoscaler/horizontal.go | 310 +++++++++++++++++- .../pkg/describe/versioned/describe.go | 26 ++ 3 files changed, 336 insertions(+), 1 deletion(-) diff --git a/pkg/apis/autoscaling/v2beta2/doc.go b/pkg/apis/autoscaling/v2beta2/doc.go index 7228e86c609..c7283aa40bb 100644 --- a/pkg/apis/autoscaling/v2beta2/doc.go +++ b/pkg/apis/autoscaling/v2beta2/doc.go @@ -17,5 +17,6 @@ limitations under the License. // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/autoscaling // +k8s:conversion-gen-external-types=k8s.io/api/autoscaling/v2beta2 // +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/autoscaling/v2beta2 package v2beta2 // import "k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2" diff --git a/pkg/controller/podautoscaler/horizontal.go b/pkg/controller/podautoscaler/horizontal.go index e2502ab4fa0..85019a4dea7 100644 --- a/pkg/controller/podautoscaler/horizontal.go +++ b/pkg/controller/podautoscaler/horizontal.go @@ -61,6 +61,12 @@ type timestampedRecommendation struct { timestamp time.Time } +type timestampedScaleEvent struct { + replicaChange int32 // positive for scaleUp, negative for scaleDown + timestamp time.Time + outdated bool +} + // HorizontalController is responsible for the synchronizing HPA objects stored // in the system with the actual deployments/replication controllers they // control. @@ -89,6 +95,10 @@ type HorizontalController struct { // Latest unstabilized recommendations for each autoscaler. recommendations map[string][]timestampedRecommendation + + // Latest autoscaler events + scaleUpEvents map[string][]timestampedScaleEvent + scaleDownEvents map[string][]timestampedScaleEvent } // NewHorizontalController creates a new HorizontalController. @@ -120,6 +130,8 @@ func NewHorizontalController( queue: workqueue.NewNamedRateLimitingQueue(NewDefaultHPARateLimiter(resyncPeriod), "horizontalpodautoscaler"), mapper: mapper, recommendations: map[string][]timestampedRecommendation{}, + scaleUpEvents: map[string][]timestampedScaleEvent{}, + scaleDownEvents: map[string][]timestampedScaleEvent{}, } hpaInformer.Informer().AddEventHandlerWithResyncPeriod( @@ -340,6 +352,8 @@ func (a *HorizontalController) reconcileKey(key string) (deleted bool, err error if errors.IsNotFound(err) { klog.Infof("Horizontal Pod Autoscaler %s has been deleted in %s", name, namespace) delete(a.recommendations, key) + delete(a.scaleUpEvents, key) + delete(a.scaleDownEvents, key) return true, nil } @@ -623,7 +637,11 @@ func (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.Ho if desiredReplicas < currentReplicas { rescaleReason = "All metrics below target" } - desiredReplicas = a.normalizeDesiredReplicas(hpa, key, currentReplicas, desiredReplicas, minReplicas) + if hpa.Spec.Behavior == nil { + desiredReplicas = a.normalizeDesiredReplicas(hpa, key, currentReplicas, desiredReplicas, minReplicas) + } else { + desiredReplicas = a.normalizeDesiredReplicasWithBehaviors(hpa, key, currentReplicas, desiredReplicas, minReplicas) + } rescale = desiredReplicas != currentReplicas } @@ -641,6 +659,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.Ho } setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "SucceededRescale", "the HPA controller was able to update the target scale to %d", desiredReplicas) a.eventRecorder.Eventf(hpa, v1.EventTypeNormal, "SuccessfulRescale", "New size: %d; reason: %s", desiredReplicas, rescaleReason) + a.storeScaleEvent(hpa.Spec.Behavior, key, currentReplicas, desiredReplicas) klog.Infof("Successful rescale of %s, old size: %d, new size: %d, reason: %s", hpa.Name, currentReplicas, desiredReplicas, rescaleReason) } else { @@ -697,6 +716,72 @@ func (a *HorizontalController) normalizeDesiredReplicas(hpa *autoscalingv2.Horiz return desiredReplicas } +// NormalizationArg is used to pass all needed information between functions as one structure +type NormalizationArg struct { + Key string + ScaleUpBehavior *autoscalingv2.HPAScalingRules + ScaleDownBehavior *autoscalingv2.HPAScalingRules + MinReplicas int32 + MaxReplicas int32 + CurrentReplicas int32 + DesiredReplicas int32 +} + +// normalizeDesiredReplicasWithBehaviors takes the metrics desired replicas value and normalizes it: +// 1. Apply the basic conditions (i.e. < maxReplicas, > minReplicas, etc...) +// 2. Apply the scale up/down limits from the hpaSpec.Behaviors (i.e. add no more than 4 pods) +// 3. Apply the constraints period (i.e. add no more than 4 pods per minute) +// 4. Apply the stabilization (i.e. add no more than 4 pods per minute, and pick the smallest recommendation during last 5 minutes) +func (a *HorizontalController) normalizeDesiredReplicasWithBehaviors(hpa *autoscalingv2.HorizontalPodAutoscaler, key string, currentReplicas, prenormalizedDesiredReplicas, minReplicas int32) int32 { + a.maybeInitScaleDownStabilizationWindow(hpa) + normalizationArg := NormalizationArg{ + Key: key, + ScaleUpBehavior: hpa.Spec.Behavior.ScaleUp, + ScaleDownBehavior: hpa.Spec.Behavior.ScaleDown, + MinReplicas: minReplicas, + MaxReplicas: hpa.Spec.MaxReplicas, + CurrentReplicas: currentReplicas, + DesiredReplicas: prenormalizedDesiredReplicas} + stabilizedRecommendation, reason, message := a.stabilizeRecommendationWithBehaviors(normalizationArg) + normalizationArg.DesiredReplicas = stabilizedRecommendation + if stabilizedRecommendation != prenormalizedDesiredReplicas { + // "ScaleUpStabilized" || "ScaleDownStabilized" + setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, reason, message) + } else { + setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ReadyForNewScale", "recommended size matches current size") + } + desiredReplicas, reason, message := a.convertDesiredReplicasWithBehaviorRate(normalizationArg) + if desiredReplicas == stabilizedRecommendation { + setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionFalse, reason, message) + } else { + setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionTrue, reason, message) + } + + return desiredReplicas +} + +func (a *HorizontalController) maybeInitScaleDownStabilizationWindow(hpa *autoscalingv2.HorizontalPodAutoscaler) { + behavior := hpa.Spec.Behavior + if behavior != nil && behavior.ScaleDown != nil && behavior.ScaleDown.StabilizationWindowSeconds == nil { + stabilizationWindowSeconds := (int32)(a.downscaleStabilisationWindow.Seconds()) + hpa.Spec.Behavior.ScaleDown.StabilizationWindowSeconds = &stabilizationWindowSeconds + } +} + +// getReplicasChangePerPeriod function find all the replica changes per period +func getReplicasChangePerPeriod(periodSeconds int32, scaleEvents []timestampedScaleEvent) int32 { + period := time.Second * time.Duration(periodSeconds) + cutoff := time.Now().Add(-period) + var replicas int32 + for _, rec := range scaleEvents { + if rec.timestamp.After(cutoff) { + replicas += rec.replicaChange + } + } + return replicas + +} + func (a *HorizontalController) getUnableComputeReplicaCountCondition(hpa *autoscalingv2.HorizontalPodAutoscaler, reason string, err error) (condition autoscalingv2.HorizontalPodAutoscalerCondition) { a.eventRecorder.Event(hpa, v1.EventTypeWarning, reason, err.Error()) return autoscalingv2.HorizontalPodAutoscalerCondition{ @@ -707,6 +792,140 @@ func (a *HorizontalController) getUnableComputeReplicaCountCondition(hpa *autosc } } +// storeScaleEvent stores (adds or replaces outdated) scale event. +// outdated events to be replaced were marked as outdated in the `markScaleEventsOutdated` function +func (a *HorizontalController) storeScaleEvent(behavior *autoscalingv2.HorizontalPodAutoscalerBehavior, key string, prevReplicas, newReplicas int32) { + if behavior == nil { + return // we should not store any event as they will not be used + } + var oldSampleIndex int + var longestPolicyPeriod int32 + foundOldSample := false + if newReplicas > prevReplicas { + longestPolicyPeriod = getLongestPolicyPeriod(behavior.ScaleUp) + markScaleEventsOutdated(a.scaleUpEvents[key], longestPolicyPeriod) + replicaChange := newReplicas - prevReplicas + for i, event := range a.scaleUpEvents[key] { + if event.outdated { + foundOldSample = true + oldSampleIndex = i + } + } + newEvent := timestampedScaleEvent{replicaChange, time.Now(), false} + if foundOldSample { + a.scaleUpEvents[key][oldSampleIndex] = newEvent + } else { + a.scaleUpEvents[key] = append(a.scaleUpEvents[key], newEvent) + } + } else { + longestPolicyPeriod = getLongestPolicyPeriod(behavior.ScaleDown) + markScaleEventsOutdated(a.scaleDownEvents[key], longestPolicyPeriod) + replicaChange := prevReplicas - newReplicas + for i, event := range a.scaleDownEvents[key] { + if event.outdated { + foundOldSample = true + oldSampleIndex = i + } + } + newEvent := timestampedScaleEvent{replicaChange, time.Now(), false} + if foundOldSample { + a.scaleDownEvents[key][oldSampleIndex] = newEvent + } else { + a.scaleDownEvents[key] = append(a.scaleDownEvents[key], newEvent) + } + } +} + +// stabilizeRecommendationWithBehaviors: +// - replaces old recommendation with the newest recommendation, +// - returns {max,min} of recommendations that are not older than constraints.Scale{Up,Down}.DelaySeconds +func (a *HorizontalController) stabilizeRecommendationWithBehaviors(args NormalizationArg) (int32, string, string) { + recommendation := args.DesiredReplicas + foundOldSample := false + oldSampleIndex := 0 + var scaleDelaySeconds int32 + var reason, message string + + var betterRecommendation func(int32, int32) int32 + + if args.DesiredReplicas >= args.CurrentReplicas { + scaleDelaySeconds = *args.ScaleUpBehavior.StabilizationWindowSeconds + betterRecommendation = min + reason = "ScaleUpStabilized" + message = "recent recommendations were lower than current one, applying the lowest recent recommendation" + } else { + scaleDelaySeconds = *args.ScaleDownBehavior.StabilizationWindowSeconds + betterRecommendation = max + reason = "ScaleDownStabilized" + message = "recent recommendations were higher than current one, applying the highest recent recommendation" + } + + maxDelaySeconds := max(*args.ScaleUpBehavior.StabilizationWindowSeconds, *args.ScaleDownBehavior.StabilizationWindowSeconds) + obsoleteCutoff := time.Now().Add(-time.Second * time.Duration(maxDelaySeconds)) + + cutoff := time.Now().Add(-time.Second * time.Duration(scaleDelaySeconds)) + for i, rec := range a.recommendations[args.Key] { + if rec.timestamp.After(cutoff) { + recommendation = betterRecommendation(rec.recommendation, recommendation) + } + if rec.timestamp.Before(obsoleteCutoff) { + foundOldSample = true + oldSampleIndex = i + } + } + if foundOldSample { + a.recommendations[args.Key][oldSampleIndex] = timestampedRecommendation{args.DesiredReplicas, time.Now()} + } else { + a.recommendations[args.Key] = append(a.recommendations[args.Key], timestampedRecommendation{args.DesiredReplicas, time.Now()}) + } + return recommendation, reason, message +} + +// convertDesiredReplicasWithBehaviorRate performs the actual normalization, given the constraint rate +// It doesn't consider the stabilizationWindow, it is done separately +func (a *HorizontalController) convertDesiredReplicasWithBehaviorRate(args NormalizationArg) (int32, string, string) { + var possibleLimitingReason, possibleLimitingMessage string + + if args.DesiredReplicas > args.CurrentReplicas { + scaleUpLimit := calculateScaleUpLimitWithScalingRules(args.CurrentReplicas, a.scaleUpEvents[args.Key], args.ScaleUpBehavior) + if scaleUpLimit < args.CurrentReplicas { + // We shouldn't scale up further until the scaleUpEvents will be cleaned up + scaleUpLimit = args.CurrentReplicas + } + maximumAllowedReplicas := args.MaxReplicas + if maximumAllowedReplicas > scaleUpLimit { + maximumAllowedReplicas = scaleUpLimit + possibleLimitingReason = "ScaleUpLimit" + possibleLimitingMessage = "the desired replica count is increasing faster than the maximum scale rate" + } else { + possibleLimitingReason = "TooManyReplicas" + possibleLimitingMessage = "the desired replica count is more than the maximum replica count" + } + if args.DesiredReplicas > maximumAllowedReplicas { + return maximumAllowedReplicas, possibleLimitingReason, possibleLimitingMessage + } + } else if args.DesiredReplicas < args.CurrentReplicas { + scaleDownLimit := calculateScaleDownLimitWithBehaviors(args.CurrentReplicas, a.scaleDownEvents[args.Key], args.ScaleDownBehavior) + if scaleDownLimit > args.CurrentReplicas { + // We shouldn't scale down further until the scaleDownEvents will be cleaned up + scaleDownLimit = args.CurrentReplicas + } + minimumAllowedReplicas := args.MinReplicas + if minimumAllowedReplicas < scaleDownLimit { + minimumAllowedReplicas = scaleDownLimit + possibleLimitingReason = "ScaleDownLimit" + possibleLimitingMessage = "the desired replica count is decreasing faster than the maximum scale rate" + } else { + possibleLimitingMessage = "the desired replica count is less than the minimum replica count" + possibleLimitingReason = "TooFewReplicas" + } + if args.DesiredReplicas < minimumAllowedReplicas { + return minimumAllowedReplicas, possibleLimitingReason, possibleLimitingMessage + } + } + return args.DesiredReplicas, "DesiredWithinRange", "the desired count is within the acceptable range" +} + // convertDesiredReplicas performs the actual normalization, without depending on `HorizontalController` or `HorizontalPodAutoscaler` func convertDesiredReplicasWithRules(currentReplicas, desiredReplicas, hpaMinReplicas, hpaMaxReplicas int32) (int32, string, string) { @@ -750,6 +969,79 @@ func calculateScaleUpLimit(currentReplicas int32) int32 { return int32(math.Max(scaleUpLimitFactor*float64(currentReplicas), scaleUpLimitMinimum)) } +// markScaleEventsOutdated set 'outdated=true' flag for all scale events that are not used by any HPA object +func markScaleEventsOutdated(scaleEvents []timestampedScaleEvent, longestPolicyPeriod int32) { + period := time.Second * time.Duration(longestPolicyPeriod) + cutoff := time.Now().Add(-period) + for i, event := range scaleEvents { + if event.timestamp.Before(cutoff) { + // outdated scale event are marked for later reuse + scaleEvents[i].outdated = true + } + } +} + +func getLongestPolicyPeriod(scalingRules *autoscalingv2.HPAScalingRules) int32 { + var longestPolicyPeriod int32 + for _, policy := range scalingRules.Policies { + if policy.PeriodSeconds > longestPolicyPeriod { + longestPolicyPeriod = policy.PeriodSeconds + } + } + return longestPolicyPeriod +} + +// calculateScaleUpLimitWithScalingRules returns the maximum number of pods that could be added for the given HPAScalingRules +func calculateScaleUpLimitWithScalingRules(currentReplicas int32, scaleEvents []timestampedScaleEvent, scalingRules *autoscalingv2.HPAScalingRules) int32 { + var result int32 = 0 + var proposed int32 + var selectPolicyFn func(int32, int32) int32 + if *scalingRules.SelectPolicy == autoscalingv2.DisabledPolicySelect { + return currentReplicas // Scaling is disabled + } else if *scalingRules.SelectPolicy == autoscalingv2.MinPolicySelect { + selectPolicyFn = min // For scaling up, the lowest change ('min' policy) produces a minimum value + } else { + selectPolicyFn = max // Use the default policy otherwise to produce a highest possible change + } + for _, policy := range scalingRules.Policies { + replicasAddedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleEvents) + periodStartReplicas := currentReplicas - replicasAddedInCurrentPeriod + if policy.Type == autoscalingv2.PodsScalingPolicy { + proposed = int32(periodStartReplicas + policy.Value) + } else if policy.Type == autoscalingv2.PercentScalingPolicy { + // the proposal has to be rounded up because the proposed change might not increase the replica count causing the target to never scale up + proposed = int32(math.Ceil(float64(periodStartReplicas) * (1 + float64(policy.Value)/100))) + } + result = selectPolicyFn(result, proposed) + } + return result +} + +// calculateScaleDownLimitWithBehavior returns the maximum number of pods that could be deleted for the given HPAScalingRules +func calculateScaleDownLimitWithBehaviors(currentReplicas int32, scaleEvents []timestampedScaleEvent, scalingRules *autoscalingv2.HPAScalingRules) int32 { + var result int32 = math.MaxInt32 + var proposed int32 + var selectPolicyFn func(int32, int32) int32 + if *scalingRules.SelectPolicy == autoscalingv2.DisabledPolicySelect { + return currentReplicas // Scaling is disabled + } else if *scalingRules.SelectPolicy == autoscalingv2.MinPolicySelect { + selectPolicyFn = max // For scaling down, the lowest change ('min' policy) produces a maximum value + } else { + selectPolicyFn = min // Use the default policy otherwise to produce a highest possible change + } + for _, policy := range scalingRules.Policies { + replicasDeletedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleEvents) + periodStartReplicas := currentReplicas + replicasDeletedInCurrentPeriod + if policy.Type == autoscalingv2.PodsScalingPolicy { + proposed = periodStartReplicas - policy.Value + } else if policy.Type == autoscalingv2.PercentScalingPolicy { + proposed = int32(float64(periodStartReplicas) * (1 - float64(policy.Value)/100)) + } + result = selectPolicyFn(result, proposed) + } + return result +} + // scaleForResourceMappings attempts to fetch the scale for the // resource with the given name and namespace, trying each RESTMapping // in turn until a working one is found. If none work, the first error @@ -885,3 +1177,19 @@ func setConditionInList(inputList []autoscalingv2.HorizontalPodAutoscalerConditi return resList } + +func max(a, b int32) int32 { + if a >= b { + return a + } else { + return b + } +} + +func min(a, b int32) int32 { + if a <= b { + return a + } else { + return b + } +} diff --git a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go index 0c0b7abb0a2..acf5ef125e1 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go @@ -3392,6 +3392,12 @@ func describeHorizontalPodAutoscalerV2beta2(hpa *autoscalingv2beta2.HorizontalPo } w.Write(LEVEL_0, "Min replicas:\t%s\n", minReplicas) w.Write(LEVEL_0, "Max replicas:\t%d\n", hpa.Spec.MaxReplicas) + // only print the hpa behavior if present + if hpa.Spec.Behavior != nil { + w.Write(LEVEL_0, "Behavior:\n") + printDirectionBehavior(w, "Scale Up", hpa.Spec.Behavior.ScaleUp) + printDirectionBehavior(w, "Scale Down", hpa.Spec.Behavior.ScaleDown) + } w.Write(LEVEL_0, "%s pods:\t", hpa.Spec.ScaleTargetRef.Kind) w.Write(LEVEL_0, "%d current / %d desired\n", hpa.Status.CurrentReplicas, hpa.Status.DesiredReplicas) @@ -3412,6 +3418,26 @@ func describeHorizontalPodAutoscalerV2beta2(hpa *autoscalingv2beta2.HorizontalPo }) } +func printDirectionBehavior(w PrefixWriter, direction string, rules *autoscalingv2beta2.HPAScalingRules) { + if rules != nil { + w.Write(LEVEL_1, "%s:\n", direction) + if rules.StabilizationWindowSeconds != nil { + w.Write(LEVEL_2, "Stabilization Window: %d seconds\n", *rules.StabilizationWindowSeconds) + } + if len(rules.Policies) > 0 { + if rules.SelectPolicy != nil { + w.Write(LEVEL_2, "Select Policy: %s\n", *rules.SelectPolicy) + } else { + w.Write(LEVEL_2, "Select Policy: %s\n", autoscalingv2beta2.MaxPolicySelect) + } + w.Write(LEVEL_2, "Policies:\n") + for _, p := range rules.Policies { + w.Write(LEVEL_3, "- Type: %s\tValue: %d\tPeriod: %d seconds\n", p.Type, p.Value, p.PeriodSeconds) + } + } + } +} + func describeHorizontalPodAutoscalerV1(hpa *autoscalingv1.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) From ac23d55d907d4f7403d75060303fa57256ecb805 Mon Sep 17 00:00:00 2001 From: Ivan Glushkov Date: Mon, 14 Oct 2019 19:30:52 +0400 Subject: [PATCH 4/5] Generates boilerplate code --- api/openapi-spec/swagger.json | 65 ++ .../autoscaling/v1/zz_generated.conversion.go | 1 + .../v2beta1/zz_generated.conversion.go | 46 +- .../v2beta2/zz_generated.conversion.go | 102 ++ .../v2beta2/zz_generated.defaults.go | 18 + pkg/apis/autoscaling/zz_generated.deepcopy.go | 78 ++ .../api/autoscaling/v2beta2/generated.pb.go | 978 ++++++++++++++++-- .../api/autoscaling/v2beta2/generated.proto | 66 ++ .../v2beta2/types_swagger_doc_generated.go | 33 + .../v2beta2/zz_generated.deepcopy.go | 78 ++ ...aling.v2beta2.HorizontalPodAutoscaler.json | 92 +- ...scaling.v2beta2.HorizontalPodAutoscaler.pb | Bin 1957 -> 2336 bytes ...aling.v2beta2.HorizontalPodAutoscaler.yaml | 80 +- 13 files changed, 1439 insertions(+), 198 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 63f047ebbd0..12a761a66cf 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -5177,6 +5177,53 @@ ], "type": "object" }, + "io.k8s.api.autoscaling.v2beta2.HPAScalingPolicy": { + "description": "HPAScalingPolicy is a single policy which must hold true for a specified past interval.", + "properties": { + "periodSeconds": { + "description": "PeriodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).", + "format": "int32", + "type": "integer" + }, + "type": { + "description": "Type is used to specify the scaling policy.", + "type": "string" + }, + "value": { + "description": "Value contains the amount of change which is permitted by the policy. It must be greater than zero", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "type", + "value", + "periodSeconds" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.HPAScalingRules": { + "description": "HPAScalingRules configures the scaling behavior for one direction. These Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.", + "properties": { + "policies": { + "description": "policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HPAScalingPolicy" + }, + "type": "array" + }, + "selectPolicy": { + "description": "selectPolicy is used to specify which policy should be used. If not set, the default value MaxPolicySelect is used.", + "type": "string" + }, + "stabilizationWindowSeconds": { + "description": "StabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down. StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler": { "description": "HorizontalPodAutoscaler is the configuration for a horizontal pod autoscaler, which automatically manages the replica count of any resource implementing the scale subresource based on the metrics specified.", "properties": { @@ -5210,6 +5257,20 @@ } ] }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerBehavior": { + "description": "HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively).", + "properties": { + "scaleDown": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HPAScalingRules", + "description": "scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e., the highest recommendation for the last 300sec is used)." + }, + "scaleUp": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HPAScalingRules", + "description": "scaleUp is scaling policy for scaling Up. If not set, the default value is the higher of:\n * increase no more than 4 pods per 60 seconds\n * double the number of pods per 60 seconds\nNo stabilization is used." + } + }, + "type": "object" + }, "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition": { "description": "HorizontalPodAutoscalerCondition describes the state of a HorizontalPodAutoscaler at a certain point.", "properties": { @@ -5278,6 +5339,10 @@ "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec": { "description": "HorizontalPodAutoscalerSpec describes the desired functionality of the HorizontalPodAutoscaler.", "properties": { + "behavior": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerBehavior", + "description": "behavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). If not set, the default HPAScalingRules for scale up and scale down are used." + }, "maxReplicas": { "description": "maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas.", "format": "int32", diff --git a/pkg/apis/autoscaling/v1/zz_generated.conversion.go b/pkg/apis/autoscaling/v1/zz_generated.conversion.go index d18511be4f1..fe74c86d314 100644 --- a/pkg/apis/autoscaling/v1/zz_generated.conversion.go +++ b/pkg/apis/autoscaling/v1/zz_generated.conversion.go @@ -403,6 +403,7 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAuto out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas)) out.MaxReplicas = in.MaxReplicas // WARNING: in.Metrics requires manual conversion: does not exist in peer-type + // WARNING: in.Behavior requires manual conversion: does not exist in peer-type return nil } diff --git a/pkg/apis/autoscaling/v2beta1/zz_generated.conversion.go b/pkg/apis/autoscaling/v2beta1/zz_generated.conversion.go index 27fdd2b6403..06e3a51654b 100644 --- a/pkg/apis/autoscaling/v2beta1/zz_generated.conversion.go +++ b/pkg/apis/autoscaling/v2beta1/zz_generated.conversion.go @@ -49,16 +49,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v2beta1.HorizontalPodAutoscaler)(nil), (*autoscaling.HorizontalPodAutoscaler)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(a.(*v2beta1.HorizontalPodAutoscaler), b.(*autoscaling.HorizontalPodAutoscaler), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*autoscaling.HorizontalPodAutoscaler)(nil), (*v2beta1.HorizontalPodAutoscaler)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(a.(*autoscaling.HorizontalPodAutoscaler), b.(*v2beta1.HorizontalPodAutoscaler), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v2beta1.HorizontalPodAutoscalerCondition)(nil), (*autoscaling.HorizontalPodAutoscalerCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v2beta1_HorizontalPodAutoscalerCondition_To_autoscaling_HorizontalPodAutoscalerCondition(a.(*v2beta1.HorizontalPodAutoscalerCondition), b.(*autoscaling.HorizontalPodAutoscalerCondition), scope) }); err != nil { @@ -84,11 +74,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*autoscaling.HorizontalPodAutoscalerSpec)(nil), (*v2beta1.HorizontalPodAutoscalerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(a.(*autoscaling.HorizontalPodAutoscalerSpec), b.(*v2beta1.HorizontalPodAutoscalerSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v2beta1.HorizontalPodAutoscalerStatus)(nil), (*autoscaling.HorizontalPodAutoscalerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v2beta1_HorizontalPodAutoscalerStatus_To_autoscaling_HorizontalPodAutoscalerStatus(a.(*v2beta1.HorizontalPodAutoscalerStatus), b.(*autoscaling.HorizontalPodAutoscalerStatus), scope) }); err != nil { @@ -129,6 +114,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*autoscaling.HorizontalPodAutoscalerSpec)(nil), (*v2beta1.HorizontalPodAutoscalerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(a.(*autoscaling.HorizontalPodAutoscalerSpec), b.(*v2beta1.HorizontalPodAutoscalerSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*autoscaling.HorizontalPodAutoscaler)(nil), (*v2beta1.HorizontalPodAutoscaler)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(a.(*autoscaling.HorizontalPodAutoscaler), b.(*v2beta1.HorizontalPodAutoscaler), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*autoscaling.MetricTarget)(nil), (*v2beta1.CrossVersionObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_autoscaling_MetricTarget_To_v2beta1_CrossVersionObjectReference(a.(*autoscaling.MetricTarget), b.(*v2beta1.CrossVersionObjectReference), scope) }); err != nil { @@ -179,6 +174,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v2beta1.HorizontalPodAutoscaler)(nil), (*autoscaling.HorizontalPodAutoscaler)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(a.(*v2beta1.HorizontalPodAutoscaler), b.(*autoscaling.HorizontalPodAutoscaler), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v2beta1.ObjectMetricSource)(nil), (*autoscaling.ObjectMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v2beta1_ObjectMetricSource_To_autoscaling_ObjectMetricSource(a.(*v2beta1.ObjectMetricSource), b.(*autoscaling.ObjectMetricSource), scope) }); err != nil { @@ -275,11 +275,6 @@ func autoConvert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAut return nil } -// Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler is an autogenerated conversion function. -func Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in *v2beta1.HorizontalPodAutoscaler, out *autoscaling.HorizontalPodAutoscaler, s conversion.Scope) error { - return autoConvert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in, out, s) -} - func autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in *autoscaling.HorizontalPodAutoscaler, out *v2beta1.HorizontalPodAutoscaler, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { @@ -291,11 +286,6 @@ func autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAut return nil } -// Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler is an autogenerated conversion function. -func Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in *autoscaling.HorizontalPodAutoscaler, out *v2beta1.HorizontalPodAutoscaler, s conversion.Scope) error { - return autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in, out, s) -} - func autoConvert_v2beta1_HorizontalPodAutoscalerCondition_To_autoscaling_HorizontalPodAutoscalerCondition(in *v2beta1.HorizontalPodAutoscalerCondition, out *autoscaling.HorizontalPodAutoscalerCondition, s conversion.Scope) error { out.Type = autoscaling.HorizontalPodAutoscalerConditionType(in.Type) out.Status = autoscaling.ConditionStatus(in.Status) @@ -408,14 +398,10 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPo } else { out.Metrics = nil } + // WARNING: in.Behavior requires manual conversion: does not exist in peer-type return nil } -// Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec is an autogenerated conversion function. -func Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in *autoscaling.HorizontalPodAutoscalerSpec, out *v2beta1.HorizontalPodAutoscalerSpec, s conversion.Scope) error { - return autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in, out, s) -} - func autoConvert_v2beta1_HorizontalPodAutoscalerStatus_To_autoscaling_HorizontalPodAutoscalerStatus(in *v2beta1.HorizontalPodAutoscalerStatus, out *autoscaling.HorizontalPodAutoscalerStatus, s conversion.Scope) error { out.ObservedGeneration = (*int64)(unsafe.Pointer(in.ObservedGeneration)) out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime)) diff --git a/pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go b/pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go index 1f2ebe74211..df8e47c5e16 100644 --- a/pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go +++ b/pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go @@ -70,6 +70,26 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v2beta2.HPAScalingPolicy)(nil), (*autoscaling.HPAScalingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2beta2_HPAScalingPolicy_To_autoscaling_HPAScalingPolicy(a.(*v2beta2.HPAScalingPolicy), b.(*autoscaling.HPAScalingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*autoscaling.HPAScalingPolicy)(nil), (*v2beta2.HPAScalingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_autoscaling_HPAScalingPolicy_To_v2beta2_HPAScalingPolicy(a.(*autoscaling.HPAScalingPolicy), b.(*v2beta2.HPAScalingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v2beta2.HPAScalingRules)(nil), (*autoscaling.HPAScalingRules)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2beta2_HPAScalingRules_To_autoscaling_HPAScalingRules(a.(*v2beta2.HPAScalingRules), b.(*autoscaling.HPAScalingRules), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*autoscaling.HPAScalingRules)(nil), (*v2beta2.HPAScalingRules)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_autoscaling_HPAScalingRules_To_v2beta2_HPAScalingRules(a.(*autoscaling.HPAScalingRules), b.(*v2beta2.HPAScalingRules), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v2beta2.HorizontalPodAutoscaler)(nil), (*autoscaling.HorizontalPodAutoscaler)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v2beta2_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(a.(*v2beta2.HorizontalPodAutoscaler), b.(*autoscaling.HorizontalPodAutoscaler), scope) }); err != nil { @@ -80,6 +100,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v2beta2.HorizontalPodAutoscalerBehavior)(nil), (*autoscaling.HorizontalPodAutoscalerBehavior)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2beta2_HorizontalPodAutoscalerBehavior_To_autoscaling_HorizontalPodAutoscalerBehavior(a.(*v2beta2.HorizontalPodAutoscalerBehavior), b.(*autoscaling.HorizontalPodAutoscalerBehavior), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*autoscaling.HorizontalPodAutoscalerBehavior)(nil), (*v2beta2.HorizontalPodAutoscalerBehavior)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_autoscaling_HorizontalPodAutoscalerBehavior_To_v2beta2_HorizontalPodAutoscalerBehavior(a.(*autoscaling.HorizontalPodAutoscalerBehavior), b.(*v2beta2.HorizontalPodAutoscalerBehavior), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v2beta2.HorizontalPodAutoscalerCondition)(nil), (*autoscaling.HorizontalPodAutoscalerCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v2beta2_HorizontalPodAutoscalerCondition_To_autoscaling_HorizontalPodAutoscalerCondition(a.(*v2beta2.HorizontalPodAutoscalerCondition), b.(*autoscaling.HorizontalPodAutoscalerCondition), scope) }); err != nil { @@ -317,6 +347,54 @@ func Convert_autoscaling_ExternalMetricStatus_To_v2beta2_ExternalMetricStatus(in return autoConvert_autoscaling_ExternalMetricStatus_To_v2beta2_ExternalMetricStatus(in, out, s) } +func autoConvert_v2beta2_HPAScalingPolicy_To_autoscaling_HPAScalingPolicy(in *v2beta2.HPAScalingPolicy, out *autoscaling.HPAScalingPolicy, s conversion.Scope) error { + out.Type = autoscaling.HPAScalingPolicyType(in.Type) + out.Value = in.Value + out.PeriodSeconds = in.PeriodSeconds + return nil +} + +// Convert_v2beta2_HPAScalingPolicy_To_autoscaling_HPAScalingPolicy is an autogenerated conversion function. +func Convert_v2beta2_HPAScalingPolicy_To_autoscaling_HPAScalingPolicy(in *v2beta2.HPAScalingPolicy, out *autoscaling.HPAScalingPolicy, s conversion.Scope) error { + return autoConvert_v2beta2_HPAScalingPolicy_To_autoscaling_HPAScalingPolicy(in, out, s) +} + +func autoConvert_autoscaling_HPAScalingPolicy_To_v2beta2_HPAScalingPolicy(in *autoscaling.HPAScalingPolicy, out *v2beta2.HPAScalingPolicy, s conversion.Scope) error { + out.Type = v2beta2.HPAScalingPolicyType(in.Type) + out.Value = in.Value + out.PeriodSeconds = in.PeriodSeconds + return nil +} + +// Convert_autoscaling_HPAScalingPolicy_To_v2beta2_HPAScalingPolicy is an autogenerated conversion function. +func Convert_autoscaling_HPAScalingPolicy_To_v2beta2_HPAScalingPolicy(in *autoscaling.HPAScalingPolicy, out *v2beta2.HPAScalingPolicy, s conversion.Scope) error { + return autoConvert_autoscaling_HPAScalingPolicy_To_v2beta2_HPAScalingPolicy(in, out, s) +} + +func autoConvert_v2beta2_HPAScalingRules_To_autoscaling_HPAScalingRules(in *v2beta2.HPAScalingRules, out *autoscaling.HPAScalingRules, s conversion.Scope) error { + out.StabilizationWindowSeconds = (*int32)(unsafe.Pointer(in.StabilizationWindowSeconds)) + out.SelectPolicy = (*autoscaling.ScalingPolicySelect)(unsafe.Pointer(in.SelectPolicy)) + out.Policies = *(*[]autoscaling.HPAScalingPolicy)(unsafe.Pointer(&in.Policies)) + return nil +} + +// Convert_v2beta2_HPAScalingRules_To_autoscaling_HPAScalingRules is an autogenerated conversion function. +func Convert_v2beta2_HPAScalingRules_To_autoscaling_HPAScalingRules(in *v2beta2.HPAScalingRules, out *autoscaling.HPAScalingRules, s conversion.Scope) error { + return autoConvert_v2beta2_HPAScalingRules_To_autoscaling_HPAScalingRules(in, out, s) +} + +func autoConvert_autoscaling_HPAScalingRules_To_v2beta2_HPAScalingRules(in *autoscaling.HPAScalingRules, out *v2beta2.HPAScalingRules, s conversion.Scope) error { + out.StabilizationWindowSeconds = (*int32)(unsafe.Pointer(in.StabilizationWindowSeconds)) + out.SelectPolicy = (*v2beta2.ScalingPolicySelect)(unsafe.Pointer(in.SelectPolicy)) + out.Policies = *(*[]v2beta2.HPAScalingPolicy)(unsafe.Pointer(&in.Policies)) + return nil +} + +// Convert_autoscaling_HPAScalingRules_To_v2beta2_HPAScalingRules is an autogenerated conversion function. +func Convert_autoscaling_HPAScalingRules_To_v2beta2_HPAScalingRules(in *autoscaling.HPAScalingRules, out *v2beta2.HPAScalingRules, s conversion.Scope) error { + return autoConvert_autoscaling_HPAScalingRules_To_v2beta2_HPAScalingRules(in, out, s) +} + func autoConvert_v2beta2_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in *v2beta2.HorizontalPodAutoscaler, out *autoscaling.HorizontalPodAutoscaler, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v2beta2_HorizontalPodAutoscalerSpec_To_autoscaling_HorizontalPodAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { @@ -349,6 +427,28 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta2_HorizontalPodAutosca return autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta2_HorizontalPodAutoscaler(in, out, s) } +func autoConvert_v2beta2_HorizontalPodAutoscalerBehavior_To_autoscaling_HorizontalPodAutoscalerBehavior(in *v2beta2.HorizontalPodAutoscalerBehavior, out *autoscaling.HorizontalPodAutoscalerBehavior, s conversion.Scope) error { + out.ScaleUp = (*autoscaling.HPAScalingRules)(unsafe.Pointer(in.ScaleUp)) + out.ScaleDown = (*autoscaling.HPAScalingRules)(unsafe.Pointer(in.ScaleDown)) + return nil +} + +// Convert_v2beta2_HorizontalPodAutoscalerBehavior_To_autoscaling_HorizontalPodAutoscalerBehavior is an autogenerated conversion function. +func Convert_v2beta2_HorizontalPodAutoscalerBehavior_To_autoscaling_HorizontalPodAutoscalerBehavior(in *v2beta2.HorizontalPodAutoscalerBehavior, out *autoscaling.HorizontalPodAutoscalerBehavior, s conversion.Scope) error { + return autoConvert_v2beta2_HorizontalPodAutoscalerBehavior_To_autoscaling_HorizontalPodAutoscalerBehavior(in, out, s) +} + +func autoConvert_autoscaling_HorizontalPodAutoscalerBehavior_To_v2beta2_HorizontalPodAutoscalerBehavior(in *autoscaling.HorizontalPodAutoscalerBehavior, out *v2beta2.HorizontalPodAutoscalerBehavior, s conversion.Scope) error { + out.ScaleUp = (*v2beta2.HPAScalingRules)(unsafe.Pointer(in.ScaleUp)) + out.ScaleDown = (*v2beta2.HPAScalingRules)(unsafe.Pointer(in.ScaleDown)) + return nil +} + +// Convert_autoscaling_HorizontalPodAutoscalerBehavior_To_v2beta2_HorizontalPodAutoscalerBehavior is an autogenerated conversion function. +func Convert_autoscaling_HorizontalPodAutoscalerBehavior_To_v2beta2_HorizontalPodAutoscalerBehavior(in *autoscaling.HorizontalPodAutoscalerBehavior, out *v2beta2.HorizontalPodAutoscalerBehavior, s conversion.Scope) error { + return autoConvert_autoscaling_HorizontalPodAutoscalerBehavior_To_v2beta2_HorizontalPodAutoscalerBehavior(in, out, s) +} + func autoConvert_v2beta2_HorizontalPodAutoscalerCondition_To_autoscaling_HorizontalPodAutoscalerCondition(in *v2beta2.HorizontalPodAutoscalerCondition, out *autoscaling.HorizontalPodAutoscalerCondition, s conversion.Scope) error { out.Type = autoscaling.HorizontalPodAutoscalerConditionType(in.Type) out.Status = autoscaling.ConditionStatus(in.Status) @@ -406,6 +506,7 @@ func autoConvert_v2beta2_HorizontalPodAutoscalerSpec_To_autoscaling_HorizontalPo out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas)) out.MaxReplicas = in.MaxReplicas out.Metrics = *(*[]autoscaling.MetricSpec)(unsafe.Pointer(&in.Metrics)) + out.Behavior = (*autoscaling.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior)) return nil } @@ -421,6 +522,7 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta2_HorizontalPo out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas)) out.MaxReplicas = in.MaxReplicas out.Metrics = *(*[]v2beta2.MetricSpec)(unsafe.Pointer(&in.Metrics)) + out.Behavior = (*v2beta2.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior)) return nil } diff --git a/pkg/apis/autoscaling/v2beta2/zz_generated.defaults.go b/pkg/apis/autoscaling/v2beta2/zz_generated.defaults.go index 8b2f839c289..f41f351bf26 100644 --- a/pkg/apis/autoscaling/v2beta2/zz_generated.defaults.go +++ b/pkg/apis/autoscaling/v2beta2/zz_generated.defaults.go @@ -21,6 +21,7 @@ limitations under the License. package v2beta2 import ( + v2beta2 "k8s.io/api/autoscaling/v2beta2" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -28,5 +29,22 @@ import ( // Public to allow building arbitrary schemes. // All generated defaulters are covering - they call all nested defaulters. func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&v2beta2.HorizontalPodAutoscaler{}, func(obj interface{}) { + SetObjectDefaults_HorizontalPodAutoscaler(obj.(*v2beta2.HorizontalPodAutoscaler)) + }) + scheme.AddTypeDefaultingFunc(&v2beta2.HorizontalPodAutoscalerList{}, func(obj interface{}) { + SetObjectDefaults_HorizontalPodAutoscalerList(obj.(*v2beta2.HorizontalPodAutoscalerList)) + }) return nil } + +func SetObjectDefaults_HorizontalPodAutoscaler(in *v2beta2.HorizontalPodAutoscaler) { + SetDefaults_HorizontalPodAutoscaler(in) +} + +func SetObjectDefaults_HorizontalPodAutoscalerList(in *v2beta2.HorizontalPodAutoscalerList) { + for i := range in.Items { + a := &in.Items[i] + SetObjectDefaults_HorizontalPodAutoscaler(a) + } +} diff --git a/pkg/apis/autoscaling/zz_generated.deepcopy.go b/pkg/apis/autoscaling/zz_generated.deepcopy.go index a6bb7229616..fbb35cd3319 100644 --- a/pkg/apis/autoscaling/zz_generated.deepcopy.go +++ b/pkg/apis/autoscaling/zz_generated.deepcopy.go @@ -77,6 +77,53 @@ func (in *ExternalMetricStatus) DeepCopy() *ExternalMetricStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HPAScalingPolicy) DeepCopyInto(out *HPAScalingPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HPAScalingPolicy. +func (in *HPAScalingPolicy) DeepCopy() *HPAScalingPolicy { + if in == nil { + return nil + } + out := new(HPAScalingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HPAScalingRules) DeepCopyInto(out *HPAScalingRules) { + *out = *in + if in.StabilizationWindowSeconds != nil { + in, out := &in.StabilizationWindowSeconds, &out.StabilizationWindowSeconds + *out = new(int32) + **out = **in + } + if in.SelectPolicy != nil { + in, out := &in.SelectPolicy, &out.SelectPolicy + *out = new(ScalingPolicySelect) + **out = **in + } + if in.Policies != nil { + in, out := &in.Policies, &out.Policies + *out = make([]HPAScalingPolicy, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HPAScalingRules. +func (in *HPAScalingRules) DeepCopy() *HPAScalingRules { + if in == nil { + return nil + } + out := new(HPAScalingRules) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizontalPodAutoscaler) DeepCopyInto(out *HorizontalPodAutoscaler) { *out = *in @@ -105,6 +152,32 @@ func (in *HorizontalPodAutoscaler) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HorizontalPodAutoscalerBehavior) DeepCopyInto(out *HorizontalPodAutoscalerBehavior) { + *out = *in + if in.ScaleUp != nil { + in, out := &in.ScaleUp, &out.ScaleUp + *out = new(HPAScalingRules) + (*in).DeepCopyInto(*out) + } + if in.ScaleDown != nil { + in, out := &in.ScaleDown, &out.ScaleDown + *out = new(HPAScalingRules) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizontalPodAutoscalerBehavior. +func (in *HorizontalPodAutoscalerBehavior) DeepCopy() *HorizontalPodAutoscalerBehavior { + if in == nil { + return nil + } + out := new(HorizontalPodAutoscalerBehavior) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizontalPodAutoscalerCondition) DeepCopyInto(out *HorizontalPodAutoscalerCondition) { *out = *in @@ -171,6 +244,11 @@ func (in *HorizontalPodAutoscalerSpec) DeepCopyInto(out *HorizontalPodAutoscaler (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Behavior != nil { + in, out := &in.Behavior, &out.Behavior + *out = new(HorizontalPodAutoscalerBehavior) + (*in).DeepCopyInto(*out) + } return } diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.pb.go b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.pb.go index 23bc5b9832a..83684b2d2fd 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.pb.go +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.pb.go @@ -131,10 +131,66 @@ func (m *ExternalMetricStatus) XXX_DiscardUnknown() { var xxx_messageInfo_ExternalMetricStatus proto.InternalMessageInfo +func (m *HPAScalingPolicy) Reset() { *m = HPAScalingPolicy{} } +func (*HPAScalingPolicy) ProtoMessage() {} +func (*HPAScalingPolicy) Descriptor() ([]byte, []int) { + return fileDescriptor_592ad94d7d6be24f, []int{3} +} +func (m *HPAScalingPolicy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HPAScalingPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *HPAScalingPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_HPAScalingPolicy.Merge(m, src) +} +func (m *HPAScalingPolicy) XXX_Size() int { + return m.Size() +} +func (m *HPAScalingPolicy) XXX_DiscardUnknown() { + xxx_messageInfo_HPAScalingPolicy.DiscardUnknown(m) +} + +var xxx_messageInfo_HPAScalingPolicy proto.InternalMessageInfo + +func (m *HPAScalingRules) Reset() { *m = HPAScalingRules{} } +func (*HPAScalingRules) ProtoMessage() {} +func (*HPAScalingRules) Descriptor() ([]byte, []int) { + return fileDescriptor_592ad94d7d6be24f, []int{4} +} +func (m *HPAScalingRules) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HPAScalingRules) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *HPAScalingRules) XXX_Merge(src proto.Message) { + xxx_messageInfo_HPAScalingRules.Merge(m, src) +} +func (m *HPAScalingRules) XXX_Size() int { + return m.Size() +} +func (m *HPAScalingRules) XXX_DiscardUnknown() { + xxx_messageInfo_HPAScalingRules.DiscardUnknown(m) +} + +var xxx_messageInfo_HPAScalingRules proto.InternalMessageInfo + func (m *HorizontalPodAutoscaler) Reset() { *m = HorizontalPodAutoscaler{} } func (*HorizontalPodAutoscaler) ProtoMessage() {} func (*HorizontalPodAutoscaler) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{3} + return fileDescriptor_592ad94d7d6be24f, []int{5} } func (m *HorizontalPodAutoscaler) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -159,10 +215,38 @@ func (m *HorizontalPodAutoscaler) XXX_DiscardUnknown() { var xxx_messageInfo_HorizontalPodAutoscaler proto.InternalMessageInfo +func (m *HorizontalPodAutoscalerBehavior) Reset() { *m = HorizontalPodAutoscalerBehavior{} } +func (*HorizontalPodAutoscalerBehavior) ProtoMessage() {} +func (*HorizontalPodAutoscalerBehavior) Descriptor() ([]byte, []int) { + return fileDescriptor_592ad94d7d6be24f, []int{6} +} +func (m *HorizontalPodAutoscalerBehavior) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HorizontalPodAutoscalerBehavior) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *HorizontalPodAutoscalerBehavior) XXX_Merge(src proto.Message) { + xxx_messageInfo_HorizontalPodAutoscalerBehavior.Merge(m, src) +} +func (m *HorizontalPodAutoscalerBehavior) XXX_Size() int { + return m.Size() +} +func (m *HorizontalPodAutoscalerBehavior) XXX_DiscardUnknown() { + xxx_messageInfo_HorizontalPodAutoscalerBehavior.DiscardUnknown(m) +} + +var xxx_messageInfo_HorizontalPodAutoscalerBehavior proto.InternalMessageInfo + func (m *HorizontalPodAutoscalerCondition) Reset() { *m = HorizontalPodAutoscalerCondition{} } func (*HorizontalPodAutoscalerCondition) ProtoMessage() {} func (*HorizontalPodAutoscalerCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{4} + return fileDescriptor_592ad94d7d6be24f, []int{7} } func (m *HorizontalPodAutoscalerCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -190,7 +274,7 @@ var xxx_messageInfo_HorizontalPodAutoscalerCondition proto.InternalMessageInfo func (m *HorizontalPodAutoscalerList) Reset() { *m = HorizontalPodAutoscalerList{} } func (*HorizontalPodAutoscalerList) ProtoMessage() {} func (*HorizontalPodAutoscalerList) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{5} + return fileDescriptor_592ad94d7d6be24f, []int{8} } func (m *HorizontalPodAutoscalerList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -218,7 +302,7 @@ var xxx_messageInfo_HorizontalPodAutoscalerList proto.InternalMessageInfo func (m *HorizontalPodAutoscalerSpec) Reset() { *m = HorizontalPodAutoscalerSpec{} } func (*HorizontalPodAutoscalerSpec) ProtoMessage() {} func (*HorizontalPodAutoscalerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{6} + return fileDescriptor_592ad94d7d6be24f, []int{9} } func (m *HorizontalPodAutoscalerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -246,7 +330,7 @@ var xxx_messageInfo_HorizontalPodAutoscalerSpec proto.InternalMessageInfo func (m *HorizontalPodAutoscalerStatus) Reset() { *m = HorizontalPodAutoscalerStatus{} } func (*HorizontalPodAutoscalerStatus) ProtoMessage() {} func (*HorizontalPodAutoscalerStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{7} + return fileDescriptor_592ad94d7d6be24f, []int{10} } func (m *HorizontalPodAutoscalerStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -274,7 +358,7 @@ var xxx_messageInfo_HorizontalPodAutoscalerStatus proto.InternalMessageInfo func (m *MetricIdentifier) Reset() { *m = MetricIdentifier{} } func (*MetricIdentifier) ProtoMessage() {} func (*MetricIdentifier) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{8} + return fileDescriptor_592ad94d7d6be24f, []int{11} } func (m *MetricIdentifier) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -302,7 +386,7 @@ var xxx_messageInfo_MetricIdentifier proto.InternalMessageInfo func (m *MetricSpec) Reset() { *m = MetricSpec{} } func (*MetricSpec) ProtoMessage() {} func (*MetricSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{9} + return fileDescriptor_592ad94d7d6be24f, []int{12} } func (m *MetricSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -330,7 +414,7 @@ var xxx_messageInfo_MetricSpec proto.InternalMessageInfo func (m *MetricStatus) Reset() { *m = MetricStatus{} } func (*MetricStatus) ProtoMessage() {} func (*MetricStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{10} + return fileDescriptor_592ad94d7d6be24f, []int{13} } func (m *MetricStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -358,7 +442,7 @@ var xxx_messageInfo_MetricStatus proto.InternalMessageInfo func (m *MetricTarget) Reset() { *m = MetricTarget{} } func (*MetricTarget) ProtoMessage() {} func (*MetricTarget) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{11} + return fileDescriptor_592ad94d7d6be24f, []int{14} } func (m *MetricTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -386,7 +470,7 @@ var xxx_messageInfo_MetricTarget proto.InternalMessageInfo func (m *MetricValueStatus) Reset() { *m = MetricValueStatus{} } func (*MetricValueStatus) ProtoMessage() {} func (*MetricValueStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{12} + return fileDescriptor_592ad94d7d6be24f, []int{15} } func (m *MetricValueStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -414,7 +498,7 @@ var xxx_messageInfo_MetricValueStatus proto.InternalMessageInfo func (m *ObjectMetricSource) Reset() { *m = ObjectMetricSource{} } func (*ObjectMetricSource) ProtoMessage() {} func (*ObjectMetricSource) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{13} + return fileDescriptor_592ad94d7d6be24f, []int{16} } func (m *ObjectMetricSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -442,7 +526,7 @@ var xxx_messageInfo_ObjectMetricSource proto.InternalMessageInfo func (m *ObjectMetricStatus) Reset() { *m = ObjectMetricStatus{} } func (*ObjectMetricStatus) ProtoMessage() {} func (*ObjectMetricStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{14} + return fileDescriptor_592ad94d7d6be24f, []int{17} } func (m *ObjectMetricStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -470,7 +554,7 @@ var xxx_messageInfo_ObjectMetricStatus proto.InternalMessageInfo func (m *PodsMetricSource) Reset() { *m = PodsMetricSource{} } func (*PodsMetricSource) ProtoMessage() {} func (*PodsMetricSource) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{15} + return fileDescriptor_592ad94d7d6be24f, []int{18} } func (m *PodsMetricSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -498,7 +582,7 @@ var xxx_messageInfo_PodsMetricSource proto.InternalMessageInfo func (m *PodsMetricStatus) Reset() { *m = PodsMetricStatus{} } func (*PodsMetricStatus) ProtoMessage() {} func (*PodsMetricStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{16} + return fileDescriptor_592ad94d7d6be24f, []int{19} } func (m *PodsMetricStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -526,7 +610,7 @@ var xxx_messageInfo_PodsMetricStatus proto.InternalMessageInfo func (m *ResourceMetricSource) Reset() { *m = ResourceMetricSource{} } func (*ResourceMetricSource) ProtoMessage() {} func (*ResourceMetricSource) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{17} + return fileDescriptor_592ad94d7d6be24f, []int{20} } func (m *ResourceMetricSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -554,7 +638,7 @@ var xxx_messageInfo_ResourceMetricSource proto.InternalMessageInfo func (m *ResourceMetricStatus) Reset() { *m = ResourceMetricStatus{} } func (*ResourceMetricStatus) ProtoMessage() {} func (*ResourceMetricStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_592ad94d7d6be24f, []int{18} + return fileDescriptor_592ad94d7d6be24f, []int{21} } func (m *ResourceMetricStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -583,7 +667,10 @@ func init() { proto.RegisterType((*CrossVersionObjectReference)(nil), "k8s.io.api.autoscaling.v2beta2.CrossVersionObjectReference") proto.RegisterType((*ExternalMetricSource)(nil), "k8s.io.api.autoscaling.v2beta2.ExternalMetricSource") proto.RegisterType((*ExternalMetricStatus)(nil), "k8s.io.api.autoscaling.v2beta2.ExternalMetricStatus") + proto.RegisterType((*HPAScalingPolicy)(nil), "k8s.io.api.autoscaling.v2beta2.HPAScalingPolicy") + proto.RegisterType((*HPAScalingRules)(nil), "k8s.io.api.autoscaling.v2beta2.HPAScalingRules") proto.RegisterType((*HorizontalPodAutoscaler)(nil), "k8s.io.api.autoscaling.v2beta2.HorizontalPodAutoscaler") + proto.RegisterType((*HorizontalPodAutoscalerBehavior)(nil), "k8s.io.api.autoscaling.v2beta2.HorizontalPodAutoscalerBehavior") proto.RegisterType((*HorizontalPodAutoscalerCondition)(nil), "k8s.io.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition") proto.RegisterType((*HorizontalPodAutoscalerList)(nil), "k8s.io.api.autoscaling.v2beta2.HorizontalPodAutoscalerList") proto.RegisterType((*HorizontalPodAutoscalerSpec)(nil), "k8s.io.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec") @@ -606,97 +693,111 @@ func init() { } var fileDescriptor_592ad94d7d6be24f = []byte{ - // 1425 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xdd, 0x6f, 0x1b, 0xc5, - 0x16, 0xcf, 0xda, 0x8e, 0x93, 0x8e, 0xd3, 0x24, 0x9d, 0x5b, 0xb5, 0x56, 0xaa, 0x6b, 0x47, 0xab, - 0xab, 0xab, 0x52, 0xd1, 0x35, 0x31, 0xe1, 0x43, 0x42, 0x48, 0xc4, 0x01, 0xda, 0x8a, 0xa4, 0x2d, - 0x93, 0xb4, 0x42, 0xa8, 0x45, 0x8c, 0x77, 0x4f, 0xdc, 0x21, 0xde, 0x5d, 0x6b, 0x76, 0x6c, 0x35, - 0x45, 0x42, 0xbc, 0xf0, 0x8e, 0x40, 0xfc, 0x13, 0x88, 0x17, 0x5e, 0x90, 0x78, 0xe4, 0x43, 0xa8, - 0x42, 0x08, 0xf5, 0xb1, 0x08, 0xc9, 0xa2, 0xe6, 0xbf, 0xe8, 0x13, 0xda, 0x99, 0xd9, 0xf5, 0xae, - 0xed, 0xc4, 0x4e, 0x95, 0x14, 0xf5, 0xcd, 0x33, 0xe7, 0x9c, 0xdf, 0xf9, 0x9c, 0x73, 0xce, 0x1a, - 0x5d, 0xda, 0x7d, 0x35, 0xb0, 0x98, 0x5f, 0xd9, 0x6d, 0xd7, 0x81, 0x7b, 0x20, 0x20, 0xa8, 0x74, - 0xc0, 0x73, 0x7c, 0x5e, 0xd1, 0x04, 0xda, 0x62, 0x15, 0xda, 0x16, 0x7e, 0x60, 0xd3, 0x26, 0xf3, - 0x1a, 0x95, 0x4e, 0xb5, 0x0e, 0x82, 0x56, 0x2b, 0x0d, 0xf0, 0x80, 0x53, 0x01, 0x8e, 0xd5, 0xe2, - 0xbe, 0xf0, 0x71, 0x49, 0xf1, 0x5b, 0xb4, 0xc5, 0xac, 0x04, 0xbf, 0xa5, 0xf9, 0x97, 0x2e, 0x36, - 0x98, 0xb8, 0xd3, 0xae, 0x5b, 0xb6, 0xef, 0x56, 0x1a, 0x7e, 0xc3, 0xaf, 0x48, 0xb1, 0x7a, 0x7b, - 0x47, 0x9e, 0xe4, 0x41, 0xfe, 0x52, 0x70, 0x4b, 0x66, 0x42, 0xbd, 0xed, 0x73, 0xa8, 0x74, 0x56, - 0x06, 0x55, 0x2e, 0xad, 0xf6, 0x79, 0x5c, 0x6a, 0xdf, 0x61, 0x1e, 0xf0, 0xbd, 0x4a, 0x6b, 0xb7, - 0x21, 0x85, 0x38, 0x04, 0x7e, 0x9b, 0xdb, 0x70, 0x28, 0xa9, 0xa0, 0xe2, 0x82, 0xa0, 0xa3, 0x74, - 0x55, 0xf6, 0x93, 0xe2, 0x6d, 0x4f, 0x30, 0x77, 0x58, 0xcd, 0xcb, 0xe3, 0x04, 0x02, 0xfb, 0x0e, - 0xb8, 0x74, 0x50, 0xce, 0xfc, 0xca, 0x40, 0xe7, 0xd6, 0xb9, 0x1f, 0x04, 0x37, 0x81, 0x07, 0xcc, - 0xf7, 0xae, 0xd5, 0x3f, 0x02, 0x5b, 0x10, 0xd8, 0x01, 0x0e, 0x9e, 0x0d, 0x78, 0x19, 0xe5, 0x76, - 0x99, 0xe7, 0x14, 0x8d, 0x65, 0xe3, 0xfc, 0x89, 0xda, 0xdc, 0xfd, 0x6e, 0x79, 0xaa, 0xd7, 0x2d, - 0xe7, 0xde, 0x61, 0x9e, 0x43, 0x24, 0x25, 0xe4, 0xf0, 0xa8, 0x0b, 0xc5, 0x4c, 0x9a, 0xe3, 0x2a, - 0x75, 0x81, 0x48, 0x0a, 0xae, 0x22, 0x44, 0x5b, 0x4c, 0x2b, 0x28, 0x66, 0x25, 0x1f, 0xd6, 0x7c, - 0x68, 0xed, 0xfa, 0x15, 0x4d, 0x21, 0x09, 0x2e, 0xf3, 0x17, 0x03, 0x9d, 0x7e, 0xeb, 0xae, 0x00, - 0xee, 0xd1, 0xe6, 0x26, 0x08, 0xce, 0xec, 0x2d, 0x19, 0x5f, 0xfc, 0x1e, 0xca, 0xbb, 0xf2, 0x2c, - 0x4d, 0x2a, 0x54, 0x5f, 0xb0, 0x0e, 0xae, 0x04, 0x4b, 0x49, 0x5f, 0x71, 0xc0, 0x13, 0x6c, 0x87, - 0x01, 0xaf, 0xcd, 0x6b, 0xd5, 0x79, 0x45, 0x21, 0x1a, 0x0f, 0x6f, 0xa3, 0xbc, 0xa0, 0xbc, 0x01, - 0x42, 0xba, 0x52, 0xa8, 0x3e, 0x3f, 0x19, 0xf2, 0xb6, 0x94, 0xe9, 0xa3, 0xaa, 0x33, 0xd1, 0x58, - 0xe6, 0xef, 0xc3, 0x8e, 0x08, 0x2a, 0xda, 0xc1, 0x31, 0x3a, 0x72, 0x0b, 0xcd, 0xd8, 0x6d, 0xce, - 0xc1, 0x8b, 0x3c, 0x59, 0x99, 0x0c, 0xfa, 0x26, 0x6d, 0xb6, 0x41, 0x59, 0x57, 0x5b, 0xd0, 0xd8, - 0x33, 0xeb, 0x0a, 0x89, 0x44, 0x90, 0xe6, 0x0f, 0x19, 0x74, 0xf6, 0xb2, 0xcf, 0xd9, 0x3d, 0xdf, - 0x13, 0xb4, 0x79, 0xdd, 0x77, 0xd6, 0x34, 0x20, 0x70, 0xfc, 0x21, 0x9a, 0x0d, 0x2b, 0xda, 0xa1, - 0x82, 0x8e, 0xf0, 0x2a, 0x2e, 0x4c, 0xab, 0xb5, 0xdb, 0x08, 0x2f, 0x02, 0x2b, 0xe4, 0xb6, 0x3a, - 0x2b, 0x96, 0x2a, 0xbb, 0x4d, 0x10, 0xb4, 0x5f, 0x19, 0xfd, 0x3b, 0x12, 0xa3, 0xe2, 0xdb, 0x28, - 0x17, 0xb4, 0xc0, 0xd6, 0x8e, 0xbd, 0x36, 0xce, 0xb1, 0x7d, 0x0c, 0xdd, 0x6a, 0x81, 0xdd, 0x2f, - 0xd5, 0xf0, 0x44, 0x24, 0x2c, 0x06, 0x94, 0x0f, 0x64, 0x00, 0x64, 0x99, 0x16, 0xaa, 0xaf, 0x3f, - 0xa9, 0x02, 0x15, 0xc5, 0x38, 0x43, 0xea, 0x4c, 0x34, 0xb8, 0xf9, 0x59, 0x16, 0x2d, 0xef, 0x23, - 0xb9, 0xee, 0x7b, 0x0e, 0x13, 0xcc, 0xf7, 0xf0, 0x65, 0x94, 0x13, 0x7b, 0x2d, 0xd0, 0x4f, 0x6f, - 0x35, 0xb2, 0x76, 0x7b, 0xaf, 0x05, 0x8f, 0xbb, 0xe5, 0xff, 0x8d, 0x93, 0x0f, 0xf9, 0x88, 0x44, - 0xc0, 0x1b, 0xb1, 0x57, 0x99, 0x14, 0x96, 0x36, 0xeb, 0x71, 0xb7, 0x3c, 0xa2, 0xff, 0x59, 0x31, - 0x52, 0xda, 0x78, 0xdc, 0x41, 0xb8, 0x49, 0x03, 0xb1, 0xcd, 0xa9, 0x17, 0x28, 0x4d, 0xcc, 0x05, - 0x1d, 0xaf, 0x0b, 0x93, 0xa5, 0x3b, 0x94, 0xa8, 0x2d, 0x69, 0x2b, 0xf0, 0xc6, 0x10, 0x1a, 0x19, - 0xa1, 0x01, 0xff, 0x1f, 0xe5, 0x39, 0xd0, 0xc0, 0xf7, 0x8a, 0x39, 0xe9, 0x45, 0x1c, 0x5c, 0x22, - 0x6f, 0x89, 0xa6, 0xe2, 0xe7, 0xd0, 0x8c, 0x0b, 0x41, 0x40, 0x1b, 0x50, 0x9c, 0x96, 0x8c, 0x71, - 0x2d, 0x6f, 0xaa, 0x6b, 0x12, 0xd1, 0xcd, 0x3f, 0x0c, 0x74, 0x6e, 0x9f, 0x38, 0x6e, 0xb0, 0x40, - 0xe0, 0x5b, 0x43, 0xf5, 0x6c, 0x4d, 0xe6, 0x60, 0x28, 0x2d, 0xab, 0x79, 0x51, 0xeb, 0x9e, 0x8d, - 0x6e, 0x12, 0xb5, 0x7c, 0x0b, 0x4d, 0x33, 0x01, 0x6e, 0x98, 0x95, 0xec, 0xf9, 0x42, 0xf5, 0x95, - 0x27, 0xac, 0xb5, 0xda, 0x49, 0xad, 0x63, 0xfa, 0x4a, 0x88, 0x46, 0x14, 0xa8, 0xf9, 0x67, 0x66, - 0x5f, 0xdf, 0xc2, 0x82, 0xc7, 0x1f, 0xa3, 0x79, 0x79, 0xd2, 0xfd, 0x0a, 0x76, 0xb4, 0x87, 0x63, - 0xdf, 0xd4, 0x01, 0xe3, 0xa2, 0x76, 0x46, 0x9b, 0x32, 0xbf, 0x95, 0x82, 0x26, 0x03, 0xaa, 0xf0, - 0x0a, 0x2a, 0xb8, 0xcc, 0x23, 0xd0, 0x6a, 0x32, 0x9b, 0xaa, 0xb2, 0x9c, 0xae, 0x2d, 0xf4, 0xba, - 0xe5, 0xc2, 0x66, 0xff, 0x9a, 0x24, 0x79, 0xf0, 0x4b, 0xa8, 0xe0, 0xd2, 0xbb, 0xb1, 0x48, 0x56, - 0x8a, 0xfc, 0x47, 0xeb, 0x2b, 0x6c, 0xf6, 0x49, 0x24, 0xc9, 0x87, 0x6f, 0x84, 0xd5, 0x10, 0x76, - 0xb7, 0xa0, 0x98, 0x93, 0x61, 0xbe, 0x30, 0x59, 0x33, 0x94, 0x2d, 0x22, 0x51, 0x39, 0x12, 0x82, - 0x44, 0x58, 0xe6, 0x77, 0x39, 0xf4, 0xdf, 0x03, 0xdf, 0x3e, 0x7e, 0x1b, 0x61, 0xbf, 0x1e, 0x00, - 0xef, 0x80, 0x73, 0x49, 0x0d, 0xdd, 0x70, 0xfa, 0x85, 0x31, 0xce, 0xd6, 0xce, 0x84, 0x65, 0x7f, - 0x6d, 0x88, 0x4a, 0x46, 0x48, 0x60, 0x1b, 0x9d, 0x0c, 0x1f, 0x83, 0x0a, 0x28, 0xd3, 0x83, 0xf6, - 0x70, 0x2f, 0xed, 0x54, 0xaf, 0x5b, 0x3e, 0xb9, 0x91, 0x04, 0x21, 0x69, 0x4c, 0xbc, 0x86, 0x16, - 0x74, 0x7f, 0x1f, 0x08, 0xf0, 0x59, 0x1d, 0x81, 0x85, 0xf5, 0x34, 0x99, 0x0c, 0xf2, 0x87, 0x10, - 0x0e, 0x04, 0x8c, 0x83, 0x13, 0x43, 0xe4, 0xd2, 0x10, 0x6f, 0xa6, 0xc9, 0x64, 0x90, 0x1f, 0x37, - 0xd1, 0xbc, 0x46, 0xd5, 0xf1, 0x2e, 0x4e, 0xcb, 0x94, 0x4d, 0x38, 0x89, 0x75, 0xd3, 0x8d, 0x6b, - 0x70, 0x3d, 0x85, 0x45, 0x06, 0xb0, 0xb1, 0x40, 0xc8, 0x8e, 0x5a, 0x5c, 0x50, 0xcc, 0x4b, 0x4d, - 0x6f, 0x3c, 0xe1, 0x1b, 0x8c, 0x7b, 0x65, 0x7f, 0x7c, 0xc5, 0x57, 0x01, 0x49, 0xe8, 0x31, 0xbf, - 0x34, 0xd0, 0xe2, 0xe0, 0x24, 0x8f, 0x77, 0x28, 0x63, 0xdf, 0x1d, 0xea, 0x36, 0x9a, 0x0d, 0xa0, - 0x09, 0xb6, 0xf0, 0xb9, 0x2e, 0x80, 0x17, 0x27, 0xec, 0x44, 0xb4, 0x0e, 0xcd, 0x2d, 0x2d, 0x5a, - 0x9b, 0x0b, 0x5b, 0x51, 0x74, 0x22, 0x31, 0xa4, 0xf9, 0x75, 0x16, 0xa1, 0x7e, 0xdd, 0xe3, 0xd5, - 0xd4, 0xe8, 0x59, 0x1e, 0x18, 0x3d, 0x8b, 0xc9, 0x85, 0x2c, 0x31, 0x66, 0x6e, 0xa2, 0xbc, 0x2f, - 0xfb, 0x81, 0xb6, 0xb0, 0x3a, 0x2e, 0x98, 0xf1, 0x84, 0x8f, 0xd1, 0x6a, 0x28, 0x6c, 0xe8, 0xba, - 0xab, 0x68, 0x34, 0x7c, 0x15, 0xe5, 0x5a, 0xbe, 0x13, 0x8d, 0xe4, 0xb1, 0x7b, 0xd2, 0x75, 0xdf, - 0x09, 0x52, 0x98, 0xb3, 0xa1, 0xed, 0xe1, 0x2d, 0x91, 0x38, 0xf8, 0x03, 0x34, 0x1b, 0xad, 0xeb, - 0xb2, 0x44, 0x0b, 0xd5, 0xd5, 0x71, 0x98, 0x44, 0xf3, 0xa7, 0x70, 0x65, 0x30, 0x23, 0x0a, 0x89, - 0x31, 0x43, 0x7c, 0xd0, 0x1b, 0x9f, 0x9c, 0x40, 0x13, 0xe0, 0x8f, 0x5a, 0x75, 0x15, 0x7e, 0x44, - 0x21, 0x31, 0xa6, 0xf9, 0x4d, 0x16, 0xcd, 0xa5, 0x56, 0xc9, 0x7f, 0x23, 0x5d, 0xea, 0xad, 0x1d, - 0x6d, 0xba, 0x14, 0xe6, 0xd1, 0xa7, 0x4b, 0xe1, 0x1e, 0x5f, 0xba, 0x12, 0xf8, 0x23, 0xd2, 0xf5, - 0x53, 0x26, 0x4a, 0x97, 0x9a, 0x7f, 0x93, 0xa5, 0x4b, 0xf1, 0x26, 0xd2, 0x75, 0x0d, 0x4d, 0x77, - 0xc2, 0x05, 0x5d, 0x67, 0xeb, 0xc0, 0x45, 0xc4, 0x8a, 0x9c, 0xb3, 0xde, 0x6d, 0x53, 0x4f, 0x30, - 0xb1, 0x57, 0x3b, 0x11, 0x2e, 0x08, 0x72, 0xc3, 0x27, 0x0a, 0x07, 0x3b, 0x68, 0x8e, 0x76, 0x80, - 0xd3, 0x06, 0xc8, 0x6b, 0x9d, 0xaf, 0xc3, 0xe2, 0x2e, 0xf6, 0xba, 0xe5, 0xb9, 0xb5, 0x04, 0x0e, - 0x49, 0xa1, 0x86, 0x63, 0x50, 0x9f, 0x6f, 0x08, 0xd6, 0x64, 0xf7, 0xd4, 0x18, 0x54, 0x93, 0x41, - 0x8e, 0xc1, 0xb5, 0x21, 0x2a, 0x19, 0x21, 0x61, 0x7e, 0x91, 0x41, 0xa7, 0x86, 0x3e, 0x53, 0xfa, - 0x41, 0x31, 0x8e, 0x29, 0x28, 0x99, 0xa7, 0x18, 0x94, 0xec, 0xa1, 0x83, 0xf2, 0x73, 0x06, 0xe1, - 0xe1, 0x26, 0x8a, 0x3f, 0x91, 0xa3, 0xd8, 0xe6, 0xac, 0x0e, 0x8e, 0x22, 0x1f, 0xc5, 0x6e, 0x97, - 0x9c, 0xe3, 0x49, 0x6c, 0x32, 0xa8, 0xec, 0x78, 0xbe, 0xa4, 0x13, 0x1f, 0xcc, 0xd9, 0xa3, 0xfd, - 0x60, 0x36, 0x7f, 0x1b, 0x0c, 0xe3, 0x33, 0xfd, 0x85, 0x3e, 0x2a, 0xfd, 0xd9, 0xa7, 0x98, 0x7e, - 0xf3, 0x47, 0x03, 0x2d, 0x0e, 0x0e, 0xe1, 0x67, 0xee, 0x7f, 0x9b, 0x5f, 0xd3, 0x4e, 0x3c, 0xdb, - 0xff, 0xd9, 0x7c, 0x6b, 0xa0, 0xd3, 0xa3, 0x56, 0x18, 0xbc, 0x9e, 0x5a, 0x3c, 0x2b, 0xc9, 0xc5, - 0xf3, 0x71, 0xb7, 0x5c, 0x1e, 0xf1, 0xaf, 0x40, 0x04, 0x93, 0xd8, 0x4d, 0x8f, 0x27, 0x01, 0xdf, - 0x0f, 0xdb, 0xac, 0x92, 0x70, 0x24, 0x36, 0x1f, 0x6b, 0xbc, 0x6b, 0x17, 0xef, 0x3f, 0x2a, 0x4d, - 0x3d, 0x78, 0x54, 0x9a, 0x7a, 0xf8, 0xa8, 0x34, 0xf5, 0x69, 0xaf, 0x64, 0xdc, 0xef, 0x95, 0x8c, - 0x07, 0xbd, 0x92, 0xf1, 0xb0, 0x57, 0x32, 0xfe, 0xea, 0x95, 0x8c, 0xcf, 0xff, 0x2e, 0x4d, 0xbd, - 0x3f, 0xa3, 0xa1, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x7e, 0xa0, 0xce, 0xf5, 0x16, 0x17, 0x00, - 0x00, + // 1657 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xdb, 0x6f, 0x1b, 0x45, + 0x17, 0xcf, 0xda, 0xce, 0x6d, 0x9c, 0x5b, 0xa7, 0xfd, 0x5a, 0x2b, 0xd5, 0x67, 0x47, 0xfb, 0x55, + 0x1f, 0x50, 0xd1, 0x35, 0x31, 0x01, 0x2a, 0x55, 0x08, 0xe2, 0x14, 0xda, 0xaa, 0x49, 0x1b, 0xc6, + 0x69, 0x40, 0x28, 0xad, 0x18, 0xef, 0x4e, 0x9c, 0x21, 0xf6, 0xae, 0xb5, 0xb3, 0x76, 0x9b, 0x22, + 0x21, 0x5e, 0x78, 0x47, 0x20, 0x5e, 0xf9, 0x03, 0x10, 0x42, 0xe2, 0x05, 0x89, 0x47, 0x2e, 0xaa, + 0x2a, 0x84, 0x50, 0xdf, 0x28, 0x2f, 0x16, 0x35, 0xff, 0x45, 0x9e, 0xd0, 0x5c, 0x76, 0xbd, 0xbb, + 0x76, 0x62, 0x27, 0x4a, 0x8a, 0xfa, 0xb6, 0x33, 0xe7, 0x9c, 0xdf, 0x99, 0x39, 0xf7, 0x59, 0x70, + 0x65, 0xfb, 0x22, 0x33, 0xa8, 0x93, 0xdf, 0x6e, 0x94, 0x89, 0x6b, 0x13, 0x8f, 0xb0, 0x7c, 0x93, + 0xd8, 0x96, 0xe3, 0xe6, 0x15, 0x01, 0xd7, 0x69, 0x1e, 0x37, 0x3c, 0x87, 0x99, 0xb8, 0x4a, 0xed, + 0x4a, 0xbe, 0x59, 0x28, 0x13, 0x0f, 0x17, 0xf2, 0x15, 0x62, 0x13, 0x17, 0x7b, 0xc4, 0x32, 0xea, + 0xae, 0xe3, 0x39, 0x30, 0x2b, 0xf9, 0x0d, 0x5c, 0xa7, 0x46, 0x88, 0xdf, 0x50, 0xfc, 0xb3, 0x17, + 0x2a, 0xd4, 0xdb, 0x6a, 0x94, 0x0d, 0xd3, 0xa9, 0xe5, 0x2b, 0x4e, 0xc5, 0xc9, 0x0b, 0xb1, 0x72, + 0x63, 0x53, 0xac, 0xc4, 0x42, 0x7c, 0x49, 0xb8, 0x59, 0x3d, 0xa4, 0xde, 0x74, 0x5c, 0x92, 0x6f, + 0xce, 0xc7, 0x55, 0xce, 0x2e, 0x74, 0x78, 0x6a, 0xd8, 0xdc, 0xa2, 0x36, 0x71, 0x77, 0xf2, 0xf5, + 0xed, 0x8a, 0x10, 0x72, 0x09, 0x73, 0x1a, 0xae, 0x49, 0x0e, 0x24, 0xc5, 0xf2, 0x35, 0xe2, 0xe1, + 0x5e, 0xba, 0xf2, 0x7b, 0x49, 0xb9, 0x0d, 0xdb, 0xa3, 0xb5, 0x6e, 0x35, 0xaf, 0xf6, 0x13, 0x60, + 0xe6, 0x16, 0xa9, 0xe1, 0xb8, 0x9c, 0xfe, 0xa5, 0x06, 0xce, 0x2e, 0xb9, 0x0e, 0x63, 0xeb, 0xc4, + 0x65, 0xd4, 0xb1, 0x6f, 0x96, 0x3f, 0x24, 0xa6, 0x87, 0xc8, 0x26, 0x71, 0x89, 0x6d, 0x12, 0x38, + 0x07, 0x52, 0xdb, 0xd4, 0xb6, 0x32, 0xda, 0x9c, 0xf6, 0xfc, 0x78, 0x71, 0xe2, 0x61, 0x2b, 0x37, + 0xd4, 0x6e, 0xe5, 0x52, 0xd7, 0xa9, 0x6d, 0x21, 0x41, 0xe1, 0x1c, 0x36, 0xae, 0x91, 0x4c, 0x22, + 0xca, 0x71, 0x03, 0xd7, 0x08, 0x12, 0x14, 0x58, 0x00, 0x00, 0xd7, 0xa9, 0x52, 0x90, 0x49, 0x0a, + 0x3e, 0xa8, 0xf8, 0xc0, 0xe2, 0xea, 0x35, 0x45, 0x41, 0x21, 0x2e, 0xfd, 0x81, 0x06, 0x4e, 0xbd, + 0x75, 0xcf, 0x23, 0xae, 0x8d, 0xab, 0x2b, 0xc4, 0x73, 0xa9, 0x59, 0x12, 0xf6, 0x85, 0xef, 0x81, + 0x91, 0x9a, 0x58, 0x8b, 0x23, 0xa5, 0x0b, 0x2f, 0x19, 0xfb, 0x47, 0x82, 0x21, 0xa5, 0xaf, 0x59, + 0xc4, 0xf6, 0xe8, 0x26, 0x25, 0x6e, 0x71, 0x4a, 0xa9, 0x1e, 0x91, 0x14, 0xa4, 0xf0, 0xe0, 0x1a, + 0x18, 0xf1, 0xb0, 0x5b, 0x21, 0x9e, 0xb8, 0x4a, 0xba, 0xf0, 0xe2, 0x60, 0xc8, 0x6b, 0x42, 0xa6, + 0x83, 0x2a, 0xd7, 0x48, 0x61, 0xe9, 0xbf, 0x77, 0x5f, 0xc4, 0xc3, 0x5e, 0x83, 0x1d, 0xe3, 0x45, + 0x36, 0xc0, 0xa8, 0xd9, 0x70, 0x5d, 0x62, 0xfb, 0x37, 0x99, 0x1f, 0x0c, 0x7a, 0x1d, 0x57, 0x1b, + 0x44, 0x9e, 0xae, 0x38, 0xad, 0xb0, 0x47, 0x97, 0x24, 0x12, 0xf2, 0x21, 0xf5, 0x6f, 0x35, 0x30, + 0x73, 0x75, 0x75, 0xb1, 0x24, 0x21, 0x56, 0x9d, 0x2a, 0x35, 0x77, 0xe0, 0x45, 0x90, 0xf2, 0x76, + 0xea, 0x44, 0x85, 0xc9, 0x39, 0x3f, 0x08, 0xd6, 0x76, 0xea, 0x64, 0xb7, 0x95, 0x3b, 0x15, 0xe7, + 0xe7, 0xfb, 0x48, 0x48, 0xc0, 0xff, 0x81, 0xe1, 0x26, 0xd7, 0x2b, 0x8e, 0x3a, 0x5c, 0x9c, 0x54, + 0xa2, 0xc3, 0xe2, 0x30, 0x48, 0xd2, 0xe0, 0x25, 0x30, 0x59, 0x27, 0x2e, 0x75, 0xac, 0x12, 0x31, + 0x1d, 0xdb, 0x62, 0x22, 0x88, 0x86, 0x8b, 0xff, 0x51, 0xcc, 0x93, 0xab, 0x61, 0x22, 0x8a, 0xf2, + 0xea, 0x5f, 0x25, 0xc0, 0x74, 0xe7, 0x00, 0xa8, 0x51, 0x25, 0x0c, 0xde, 0x01, 0xb3, 0xcc, 0xc3, + 0x65, 0x5a, 0xa5, 0xf7, 0xb1, 0x47, 0x1d, 0xfb, 0x5d, 0x6a, 0x5b, 0xce, 0xdd, 0x28, 0x7a, 0xb6, + 0xdd, 0xca, 0xcd, 0x96, 0xf6, 0xe4, 0x42, 0xfb, 0x20, 0xc0, 0xeb, 0x60, 0x82, 0x91, 0x2a, 0x31, + 0x3d, 0x79, 0x5f, 0x65, 0x97, 0xe7, 0xda, 0xad, 0xdc, 0x44, 0x29, 0xb4, 0xbf, 0xdb, 0xca, 0x9d, + 0x8c, 0x18, 0x46, 0x12, 0x51, 0x44, 0x18, 0xde, 0x01, 0x63, 0x75, 0xfe, 0x45, 0x09, 0xcb, 0x24, + 0xe6, 0x92, 0x83, 0xc4, 0x4a, 0xdc, 0xe0, 0xc5, 0x19, 0x65, 0xaa, 0xb1, 0x55, 0x85, 0x84, 0x02, + 0x4c, 0xfd, 0xc7, 0x04, 0x38, 0x73, 0xd5, 0x71, 0xe9, 0x7d, 0xc7, 0xf6, 0x70, 0x75, 0xd5, 0xb1, + 0x16, 0x15, 0x22, 0x71, 0xe1, 0x07, 0x60, 0x8c, 0xd7, 0x28, 0x0b, 0x7b, 0xb8, 0x47, 0x9c, 0x06, + 0xa5, 0xc6, 0xa8, 0x6f, 0x57, 0xf8, 0x06, 0x33, 0x38, 0xb7, 0xd1, 0x9c, 0x37, 0x64, 0x21, 0x59, + 0x21, 0x1e, 0xee, 0xe4, 0x7a, 0x67, 0x0f, 0x05, 0xa8, 0xf0, 0x36, 0x48, 0xb1, 0x3a, 0x31, 0x55, + 0xa8, 0x5e, 0xea, 0x7b, 0xb3, 0xde, 0x07, 0x2d, 0xd5, 0x89, 0xd9, 0x29, 0x3e, 0x7c, 0x85, 0x04, + 0x2c, 0x24, 0x60, 0x84, 0x89, 0x90, 0x16, 0x5e, 0x4d, 0x17, 0x5e, 0x3f, 0xac, 0x02, 0x99, 0x17, + 0x41, 0xce, 0xc9, 0x35, 0x52, 0xe0, 0xfa, 0x1f, 0x1a, 0xc8, 0xed, 0x21, 0x59, 0x24, 0x5b, 0xb8, + 0x49, 0x1d, 0x17, 0xae, 0x83, 0x51, 0xb1, 0x73, 0xab, 0xae, 0x4c, 0x99, 0x1f, 0xdc, 0x8d, 0x22, + 0x6c, 0x8b, 0x69, 0x9e, 0x91, 0x25, 0x89, 0x81, 0x7c, 0x30, 0xb8, 0x01, 0xc6, 0xc5, 0xe7, 0x65, + 0xe7, 0xae, 0xad, 0xcc, 0x78, 0x60, 0xe4, 0xc9, 0x76, 0x2b, 0x37, 0x5e, 0xf2, 0x51, 0x50, 0x07, + 0x50, 0xff, 0x34, 0x09, 0xe6, 0xf6, 0xb8, 0xd9, 0x92, 0x63, 0x5b, 0x94, 0x07, 0x3f, 0xbc, 0x1a, + 0xc9, 0xff, 0x85, 0x58, 0xfe, 0x9f, 0xeb, 0x27, 0x1f, 0xaa, 0x07, 0xcb, 0x81, 0xbf, 0x12, 0x11, + 0x2c, 0x65, 0xf0, 0xdd, 0x56, 0xae, 0x47, 0xaf, 0x36, 0x02, 0xa4, 0xa8, 0x5b, 0x60, 0x13, 0xc0, + 0x2a, 0x66, 0xde, 0x9a, 0x8b, 0x6d, 0x26, 0x35, 0xd1, 0x1a, 0x51, 0x91, 0x70, 0x7e, 0xb0, 0x40, + 0xe6, 0x12, 0xc5, 0x59, 0x75, 0x0a, 0xb8, 0xdc, 0x85, 0x86, 0x7a, 0x68, 0x80, 0xff, 0x07, 0x23, + 0x2e, 0xc1, 0xcc, 0xb1, 0x33, 0x29, 0x71, 0x8b, 0x20, 0x6c, 0x90, 0xd8, 0x45, 0x8a, 0x0a, 0x5f, + 0x00, 0xa3, 0x35, 0xc2, 0x18, 0xae, 0x90, 0xcc, 0xb0, 0x60, 0x0c, 0xea, 0xee, 0x8a, 0xdc, 0x46, + 0x3e, 0x5d, 0xff, 0x53, 0x03, 0x67, 0xf7, 0xb0, 0xe3, 0x32, 0x65, 0x1e, 0xdc, 0xe8, 0xca, 0x54, + 0x63, 0xb0, 0x0b, 0x72, 0x69, 0x91, 0xa7, 0x41, 0x8d, 0xf0, 0x77, 0x42, 0x59, 0xba, 0x01, 0x86, + 0xa9, 0x47, 0x6a, 0x7e, 0x01, 0x7a, 0xed, 0x90, 0x59, 0xd4, 0xa9, 0xef, 0xd7, 0x38, 0x1a, 0x92, + 0xa0, 0xfa, 0x83, 0xe4, 0x9e, 0x77, 0xe3, 0xa9, 0x0c, 0x3f, 0x02, 0x53, 0x62, 0xa5, 0x7a, 0x2b, + 0xd9, 0x54, 0x37, 0xec, 0x5b, 0x2d, 0xf6, 0x19, 0x6d, 0x8a, 0xa7, 0xd5, 0x51, 0xa6, 0x4a, 0x11, + 0x68, 0x14, 0x53, 0x05, 0xe7, 0x41, 0xba, 0x46, 0x6d, 0x44, 0xea, 0x55, 0x6a, 0x62, 0xa6, 0xfa, + 0xd4, 0x74, 0xbb, 0x95, 0x4b, 0xaf, 0x74, 0xb6, 0x51, 0x98, 0x07, 0xbe, 0x02, 0xd2, 0x35, 0x7c, + 0x2f, 0x10, 0x91, 0xfd, 0xe4, 0xa4, 0xd2, 0x97, 0x5e, 0xe9, 0x90, 0x50, 0x98, 0x0f, 0xde, 0xe2, + 0xd1, 0xc0, 0x3b, 0x31, 0xcb, 0xa4, 0x84, 0x99, 0xcf, 0x0f, 0xd6, 0xb8, 0x45, 0xf1, 0x0b, 0x45, + 0x8e, 0x80, 0x40, 0x3e, 0x16, 0xa4, 0x60, 0xac, 0xac, 0x6a, 0x90, 0x88, 0xb2, 0x74, 0xe1, 0x8d, + 0xc3, 0xba, 0x4f, 0xc1, 0x14, 0x27, 0x78, 0x98, 0xf8, 0x2b, 0x14, 0xc0, 0xeb, 0xdf, 0xa7, 0xc0, + 0x7f, 0xf7, 0x2d, 0xa0, 0xf0, 0x6d, 0x00, 0x9d, 0x32, 0x23, 0x6e, 0x93, 0x58, 0x57, 0xe4, 0x2c, + 0xca, 0x87, 0x42, 0xee, 0xce, 0x64, 0xf1, 0x34, 0xcf, 0xb0, 0x9b, 0x5d, 0x54, 0xd4, 0x43, 0x02, + 0x9a, 0x60, 0x92, 0xe7, 0x9d, 0xf4, 0x1d, 0x55, 0xf3, 0xe7, 0xc1, 0x92, 0xfa, 0x04, 0x1f, 0x1d, + 0x96, 0xc3, 0x20, 0x28, 0x8a, 0x09, 0x17, 0xc1, 0xb4, 0x1a, 0x7b, 0x62, 0xbe, 0x3c, 0xa3, 0x8c, + 0x3d, 0xbd, 0x14, 0x25, 0xa3, 0x38, 0x3f, 0x87, 0xb0, 0x08, 0xa3, 0x2e, 0xb1, 0x02, 0x88, 0x54, + 0x14, 0xe2, 0x72, 0x94, 0x8c, 0xe2, 0xfc, 0xb0, 0x0a, 0xa6, 0x14, 0xaa, 0x72, 0x6d, 0x66, 0x58, + 0x44, 0xc7, 0x80, 0x03, 0xaa, 0xea, 0x5c, 0x41, 0xb8, 0x2f, 0x45, 0xb0, 0x50, 0x0c, 0x1b, 0x7a, + 0x00, 0x98, 0x7e, 0x35, 0x65, 0x99, 0x11, 0xa1, 0xe9, 0xcd, 0x43, 0xc6, 0x4b, 0x50, 0x96, 0x3b, + 0x33, 0x40, 0xb0, 0xc5, 0x50, 0x48, 0x8f, 0xfe, 0x85, 0x06, 0x66, 0xe2, 0x03, 0x6e, 0xf0, 0xb4, + 0xd0, 0xf6, 0x7c, 0x5a, 0xdc, 0x06, 0x63, 0x72, 0x54, 0x72, 0x5c, 0x15, 0x00, 0x2f, 0x0f, 0x58, + 0xf4, 0x70, 0x99, 0x54, 0x4b, 0x4a, 0x54, 0x86, 0xb3, 0xbf, 0x42, 0x01, 0xa4, 0xfe, 0x75, 0x12, + 0x80, 0x4e, 0x8a, 0xc1, 0x85, 0x48, 0x97, 0x9b, 0x8b, 0x75, 0xb9, 0x99, 0xf0, 0x3b, 0x25, 0xd4, + 0xd1, 0xd6, 0xc1, 0x88, 0x23, 0x4a, 0x8f, 0x3a, 0x61, 0xa1, 0x9f, 0x31, 0x83, 0x31, 0x29, 0x40, + 0x2b, 0x02, 0xde, 0x3b, 0x54, 0x01, 0x53, 0x68, 0xf0, 0x06, 0x48, 0xd5, 0x1d, 0xcb, 0x9f, 0x6b, + 0xfa, 0x8e, 0x84, 0xab, 0x8e, 0xc5, 0x22, 0x98, 0x63, 0xfc, 0xec, 0x7c, 0x17, 0x09, 0x1c, 0x3e, + 0x66, 0xfa, 0xaf, 0x58, 0x11, 0xa2, 0xe9, 0xc2, 0x42, 0x3f, 0x4c, 0xa4, 0xf8, 0x23, 0xb8, 0xc2, + 0x98, 0x3e, 0x05, 0x05, 0x98, 0x1c, 0x9f, 0xa8, 0x87, 0x90, 0x2a, 0x43, 0x7d, 0xf1, 0x7b, 0xbd, + 0x00, 0x25, 0xbe, 0x4f, 0x41, 0x01, 0xa6, 0xfe, 0x4d, 0x12, 0x4c, 0x44, 0x5e, 0x58, 0xff, 0x86, + 0xbb, 0x64, 0xae, 0x1d, 0xad, 0xbb, 0x24, 0xe6, 0xd1, 0xbb, 0x4b, 0xe2, 0x1e, 0x9f, 0xbb, 0x42, + 0xf8, 0x3d, 0xdc, 0xf5, 0x73, 0xc2, 0x77, 0x97, 0x6c, 0xb5, 0x83, 0xb9, 0x4b, 0xf2, 0x86, 0xdc, + 0x75, 0x33, 0xfc, 0x7e, 0xec, 0x33, 0xf3, 0x18, 0xfe, 0xe5, 0x8c, 0x77, 0x1a, 0xd8, 0xf6, 0xa8, + 0xb7, 0x53, 0x1c, 0xef, 0x7a, 0x6b, 0x5a, 0x60, 0x02, 0x37, 0x89, 0x8b, 0x2b, 0x44, 0x6c, 0x2b, + 0x7f, 0x1d, 0x14, 0x77, 0x86, 0x3f, 0xf5, 0x16, 0x43, 0x38, 0x28, 0x82, 0xca, 0xdb, 0xa0, 0x5a, + 0xdf, 0xf2, 0x82, 0x37, 0xa4, 0xea, 0x0c, 0xa2, 0x0d, 0x2e, 0x76, 0x51, 0x51, 0x0f, 0x09, 0xfd, + 0xf3, 0x04, 0x38, 0xd1, 0xf5, 0x7a, 0xef, 0x18, 0x45, 0x3b, 0x26, 0xa3, 0x24, 0x9e, 0xa2, 0x51, + 0x92, 0x07, 0x36, 0xca, 0x2f, 0x09, 0x00, 0xbb, 0x8b, 0x28, 0xfc, 0x58, 0xb4, 0x62, 0xd3, 0xa5, + 0x65, 0x62, 0x49, 0xf2, 0x51, 0x8c, 0x91, 0xe1, 0x3e, 0x1e, 0xc6, 0x46, 0x71, 0x65, 0xc7, 0xf3, + 0x83, 0x29, 0xf4, 0x1f, 0x29, 0x79, 0xb4, 0xff, 0x91, 0xf4, 0xdf, 0xe2, 0x66, 0x7c, 0xa6, 0x7f, + 0x5c, 0xf5, 0x72, 0x7f, 0xf2, 0x29, 0xba, 0x5f, 0xff, 0x49, 0x03, 0x33, 0xf1, 0x26, 0xfc, 0xcc, + 0xfd, 0xce, 0xfc, 0x35, 0x7a, 0x89, 0x67, 0xfb, 0x57, 0xe6, 0x77, 0x1a, 0x38, 0xd5, 0x6b, 0x84, + 0x81, 0x4b, 0x91, 0xc1, 0x33, 0x1f, 0x1e, 0x3c, 0x77, 0x5b, 0xb9, 0x5c, 0x8f, 0x1f, 0x10, 0x3e, + 0x4c, 0x68, 0x36, 0x3d, 0x1e, 0x07, 0xfc, 0xd0, 0x7d, 0x66, 0xe9, 0x84, 0x23, 0x39, 0xf3, 0xb1, + 0xda, 0xbb, 0x78, 0xe1, 0xe1, 0x93, 0xec, 0xd0, 0xa3, 0x27, 0xd9, 0xa1, 0xc7, 0x4f, 0xb2, 0x43, + 0x9f, 0xb4, 0xb3, 0xda, 0xc3, 0x76, 0x56, 0x7b, 0xd4, 0xce, 0x6a, 0x8f, 0xdb, 0x59, 0xed, 0xaf, + 0x76, 0x56, 0xfb, 0xec, 0xef, 0xec, 0xd0, 0xfb, 0xa3, 0x0a, 0xfa, 0x9f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x79, 0xae, 0x08, 0x04, 0x2d, 0x1a, 0x00, 0x00, } func (m *CrossVersionObjectReference) Marshal() (dAtA []byte, err error) { @@ -823,6 +924,89 @@ func (m *ExternalMetricStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *HPAScalingPolicy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HPAScalingPolicy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HPAScalingPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i = encodeVarintGenerated(dAtA, i, uint64(m.PeriodSeconds)) + i-- + dAtA[i] = 0x18 + i = encodeVarintGenerated(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x10 + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *HPAScalingRules) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HPAScalingRules) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HPAScalingRules) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.StabilizationWindowSeconds != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.StabilizationWindowSeconds)) + i-- + dAtA[i] = 0x18 + } + if len(m.Policies) > 0 { + for iNdEx := len(m.Policies) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Policies[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.SelectPolicy != nil { + i -= len(*m.SelectPolicy) + copy(dAtA[i:], *m.SelectPolicy) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.SelectPolicy))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HorizontalPodAutoscaler) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -876,6 +1060,53 @@ func (m *HorizontalPodAutoscaler) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *HorizontalPodAutoscalerBehavior) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HorizontalPodAutoscalerBehavior) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HorizontalPodAutoscalerBehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ScaleDown != nil { + { + size, err := m.ScaleDown.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ScaleUp != nil { + { + size, err := m.ScaleUp.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HorizontalPodAutoscalerCondition) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -996,6 +1227,18 @@ func (m *HorizontalPodAutoscalerSpec) MarshalToSizedBuffer(dAtA []byte) (int, er _ = i var l int _ = l + if m.Behavior != nil { + { + size, err := m.Behavior.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } if len(m.Metrics) > 0 { for iNdEx := len(m.Metrics) - 1; iNdEx >= 0; iNdEx-- { { @@ -1726,6 +1969,41 @@ func (m *ExternalMetricStatus) Size() (n int) { return n } +func (m *HPAScalingPolicy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + n += 1 + l + sovGenerated(uint64(l)) + n += 1 + sovGenerated(uint64(m.Value)) + n += 1 + sovGenerated(uint64(m.PeriodSeconds)) + return n +} + +func (m *HPAScalingRules) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SelectPolicy != nil { + l = len(*m.SelectPolicy) + n += 1 + l + sovGenerated(uint64(l)) + } + if len(m.Policies) > 0 { + for _, e := range m.Policies { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if m.StabilizationWindowSeconds != nil { + n += 1 + sovGenerated(uint64(*m.StabilizationWindowSeconds)) + } + return n +} + func (m *HorizontalPodAutoscaler) Size() (n int) { if m == nil { return 0 @@ -1741,6 +2019,23 @@ func (m *HorizontalPodAutoscaler) Size() (n int) { return n } +func (m *HorizontalPodAutoscalerBehavior) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ScaleUp != nil { + l = m.ScaleUp.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.ScaleDown != nil { + l = m.ScaleDown.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *HorizontalPodAutoscalerCondition) Size() (n int) { if m == nil { return 0 @@ -1795,6 +2090,10 @@ func (m *HorizontalPodAutoscalerSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.Behavior != nil { + l = m.Behavior.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -2061,6 +2360,35 @@ func (this *ExternalMetricStatus) String() string { }, "") return s } +func (this *HPAScalingPolicy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HPAScalingPolicy{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `PeriodSeconds:` + fmt.Sprintf("%v", this.PeriodSeconds) + `,`, + `}`, + }, "") + return s +} +func (this *HPAScalingRules) String() string { + if this == nil { + return "nil" + } + repeatedStringForPolicies := "[]HPAScalingPolicy{" + for _, f := range this.Policies { + repeatedStringForPolicies += strings.Replace(strings.Replace(f.String(), "HPAScalingPolicy", "HPAScalingPolicy", 1), `&`, ``, 1) + "," + } + repeatedStringForPolicies += "}" + s := strings.Join([]string{`&HPAScalingRules{`, + `SelectPolicy:` + valueToStringGenerated(this.SelectPolicy) + `,`, + `Policies:` + repeatedStringForPolicies + `,`, + `StabilizationWindowSeconds:` + valueToStringGenerated(this.StabilizationWindowSeconds) + `,`, + `}`, + }, "") + return s +} func (this *HorizontalPodAutoscaler) String() string { if this == nil { return "nil" @@ -2073,6 +2401,17 @@ func (this *HorizontalPodAutoscaler) String() string { }, "") return s } +func (this *HorizontalPodAutoscalerBehavior) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HorizontalPodAutoscalerBehavior{`, + `ScaleUp:` + strings.Replace(this.ScaleUp.String(), "HPAScalingRules", "HPAScalingRules", 1) + `,`, + `ScaleDown:` + strings.Replace(this.ScaleDown.String(), "HPAScalingRules", "HPAScalingRules", 1) + `,`, + `}`, + }, "") + return s +} func (this *HorizontalPodAutoscalerCondition) String() string { if this == nil { return "nil" @@ -2117,6 +2456,7 @@ func (this *HorizontalPodAutoscalerSpec) String() string { `MinReplicas:` + valueToStringGenerated(this.MinReplicas) + `,`, `MaxReplicas:` + fmt.Sprintf("%v", this.MaxReplicas) + `,`, `Metrics:` + repeatedStringForMetrics + `,`, + `Behavior:` + strings.Replace(this.Behavior.String(), "HorizontalPodAutoscalerBehavior", "HorizontalPodAutoscalerBehavior", 1) + `,`, `}`, }, "") return s @@ -2673,6 +3013,269 @@ func (m *ExternalMetricStatus) Unmarshal(dAtA []byte) error { } return nil } +func (m *HPAScalingPolicy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HPAScalingPolicy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HPAScalingPolicy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = HPAScalingPolicyType(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + m.Value = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Value |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodSeconds", wireType) + } + m.PeriodSeconds = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PeriodSeconds |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HPAScalingRules) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HPAScalingRules: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HPAScalingRules: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SelectPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := ScalingPolicySelect(dAtA[iNdEx:postIndex]) + m.SelectPolicy = &s + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Policies = append(m.Policies, HPAScalingPolicy{}) + if err := m.Policies[len(m.Policies)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StabilizationWindowSeconds", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.StabilizationWindowSeconds = &v + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HorizontalPodAutoscaler) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2825,6 +3428,131 @@ func (m *HorizontalPodAutoscaler) Unmarshal(dAtA []byte) error { } return nil } +func (m *HorizontalPodAutoscalerBehavior) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HorizontalPodAutoscalerBehavior: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HorizontalPodAutoscalerBehavior: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ScaleUp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ScaleUp == nil { + m.ScaleUp = &HPAScalingRules{} + } + if err := m.ScaleUp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ScaleDown", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ScaleDown == nil { + m.ScaleDown = &HPAScalingRules{} + } + if err := m.ScaleDown.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HorizontalPodAutoscalerCondition) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3294,6 +4022,42 @@ func (m *HorizontalPodAutoscalerSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Behavior", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Behavior == nil { + m.Behavior = &HorizontalPodAutoscalerBehavior{} + } + if err := m.Behavior.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto index 80f1d345d44..24dc5882e75 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto @@ -64,6 +64,47 @@ message ExternalMetricStatus { optional MetricValueStatus current = 2; } +// HPAScalingPolicy is a single policy which must hold true for a specified past interval. +message HPAScalingPolicy { + // Type is used to specify the scaling policy. + optional string type = 1; + + // Value contains the amount of change which is permitted by the policy. + // It must be greater than zero + optional int32 value = 2; + + // PeriodSeconds specifies the window of time for which the policy should hold true. + // PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min). + optional int32 periodSeconds = 3; +} + +// HPAScalingRules configures the scaling behavior for one direction. +// These Rules are applied after calculating DesiredReplicas from metrics for the HPA. +// They can limit the scaling velocity by specifying scaling policies. +// They can prevent flapping by specifying the stabilization window, so that the +// number of replicas is not set instantly, instead, the safest value from the stabilization +// window is chosen. +message HPAScalingRules { + // StabilizationWindowSeconds is the number of seconds for which past recommendations should be + // considered while scaling up or scaling down. + // StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). + // If not set, use the default values: + // - For scale up: 0 (i.e. no stabilization is done). + // - For scale down: 300 (i.e. the stabilization window is 300 seconds long). + // +optional + optional int32 stabilizationWindowSeconds = 3; + + // selectPolicy is used to specify which policy should be used. + // If not set, the default value MaxPolicySelect is used. + // +optional + optional string selectPolicy = 1; + + // policies is a list of potential scaling polices which can be used during scaling. + // At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid + // +optional + repeated HPAScalingPolicy policies = 2; +} + // HorizontalPodAutoscaler is the configuration for a horizontal pod // autoscaler, which automatically manages the replica count of any resource // implementing the scale subresource based on the metrics specified. @@ -83,6 +124,25 @@ message HorizontalPodAutoscaler { optional HorizontalPodAutoscalerStatus status = 3; } +// HorizontalPodAutoscalerBehavior configures the scaling behavior of the target +// in both Up and Down directions (scaleUp and scaleDown fields respectively). +message HorizontalPodAutoscalerBehavior { + // scaleUp is scaling policy for scaling Up. + // If not set, the default value is the higher of: + // * increase no more than 4 pods per 60 seconds + // * double the number of pods per 60 seconds + // No stabilization is used. + // +optional + optional HPAScalingRules scaleUp = 1; + + // scaleDown is scaling policy for scaling Down. + // If not set, the default value is to allow to scale down to minReplicas pods, with a + // 300 second stabilization window (i.e., the highest recommendation for + // the last 300sec is used). + // +optional + optional HPAScalingRules scaleDown = 2; +} + // HorizontalPodAutoscalerCondition describes the state of // a HorizontalPodAutoscaler at a certain point. message HorizontalPodAutoscalerCondition { @@ -145,6 +205,12 @@ message HorizontalPodAutoscalerSpec { // If not set, the default metric will be set to 80% average CPU utilization. // +optional repeated MetricSpec metrics = 4; + + // behavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // +optional + optional HorizontalPodAutoscalerBehavior behavior = 5; } // HorizontalPodAutoscalerStatus describes the current status of a horizontal pod autoscaler. diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/types_swagger_doc_generated.go b/staging/src/k8s.io/api/autoscaling/v2beta2/types_swagger_doc_generated.go index bb85b9f0f45..3f38880f952 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/types_swagger_doc_generated.go @@ -58,6 +58,28 @@ func (ExternalMetricStatus) SwaggerDoc() map[string]string { return map_ExternalMetricStatus } +var map_HPAScalingPolicy = map[string]string{ + "": "HPAScalingPolicy is a single policy which must hold true for a specified past interval.", + "type": "Type is used to specify the scaling policy.", + "value": "Value contains the amount of change which is permitted by the policy. It must be greater than zero", + "periodSeconds": "PeriodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).", +} + +func (HPAScalingPolicy) SwaggerDoc() map[string]string { + return map_HPAScalingPolicy +} + +var map_HPAScalingRules = map[string]string{ + "": "HPAScalingRules configures the scaling behavior for one direction. These Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.", + "stabilizationWindowSeconds": "StabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down. StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).", + "selectPolicy": "selectPolicy is used to specify which policy should be used. If not set, the default value MaxPolicySelect is used.", + "policies": "policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid", +} + +func (HPAScalingRules) SwaggerDoc() map[string]string { + return map_HPAScalingRules +} + var map_HorizontalPodAutoscaler = map[string]string{ "": "HorizontalPodAutoscaler is the configuration for a horizontal pod autoscaler, which automatically manages the replica count of any resource implementing the scale subresource based on the metrics specified.", "metadata": "metadata is the standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", @@ -69,6 +91,16 @@ func (HorizontalPodAutoscaler) SwaggerDoc() map[string]string { return map_HorizontalPodAutoscaler } +var map_HorizontalPodAutoscalerBehavior = map[string]string{ + "": "HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively).", + "scaleUp": "scaleUp is scaling policy for scaling Up. If not set, the default value is the higher of:\n * increase no more than 4 pods per 60 seconds\n * double the number of pods per 60 seconds\nNo stabilization is used.", + "scaleDown": "scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e., the highest recommendation for the last 300sec is used).", +} + +func (HorizontalPodAutoscalerBehavior) SwaggerDoc() map[string]string { + return map_HorizontalPodAutoscalerBehavior +} + var map_HorizontalPodAutoscalerCondition = map[string]string{ "": "HorizontalPodAutoscalerCondition describes the state of a HorizontalPodAutoscaler at a certain point.", "type": "type describes the current condition", @@ -98,6 +130,7 @@ var map_HorizontalPodAutoscalerSpec = map[string]string{ "minReplicas": "minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the alpha feature gate HPAScaleToZero is enabled and at least one Object or External metric is configured. Scaling is active as long as at least one metric value is available.", "maxReplicas": "maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas.", "metrics": "metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). The desired replica count is calculated multiplying the ratio between the target value and the current value by the current number of pods. Ergo, metrics used must decrease as the pod count is increased, and vice-versa. See the individual metric source types for more information about how each type of metric must respond. If not set, the default metric will be set to 80% average CPU utilization.", + "behavior": "behavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). If not set, the default HPAScalingRules for scale up and scale down are used.", } func (HorizontalPodAutoscalerSpec) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/zz_generated.deepcopy.go b/staging/src/k8s.io/api/autoscaling/v2beta2/zz_generated.deepcopy.go index 2dffa333608..ca26fe92065 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/zz_generated.deepcopy.go @@ -77,6 +77,53 @@ func (in *ExternalMetricStatus) DeepCopy() *ExternalMetricStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HPAScalingPolicy) DeepCopyInto(out *HPAScalingPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HPAScalingPolicy. +func (in *HPAScalingPolicy) DeepCopy() *HPAScalingPolicy { + if in == nil { + return nil + } + out := new(HPAScalingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HPAScalingRules) DeepCopyInto(out *HPAScalingRules) { + *out = *in + if in.StabilizationWindowSeconds != nil { + in, out := &in.StabilizationWindowSeconds, &out.StabilizationWindowSeconds + *out = new(int32) + **out = **in + } + if in.SelectPolicy != nil { + in, out := &in.SelectPolicy, &out.SelectPolicy + *out = new(ScalingPolicySelect) + **out = **in + } + if in.Policies != nil { + in, out := &in.Policies, &out.Policies + *out = make([]HPAScalingPolicy, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HPAScalingRules. +func (in *HPAScalingRules) DeepCopy() *HPAScalingRules { + if in == nil { + return nil + } + out := new(HPAScalingRules) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizontalPodAutoscaler) DeepCopyInto(out *HorizontalPodAutoscaler) { *out = *in @@ -105,6 +152,32 @@ func (in *HorizontalPodAutoscaler) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HorizontalPodAutoscalerBehavior) DeepCopyInto(out *HorizontalPodAutoscalerBehavior) { + *out = *in + if in.ScaleUp != nil { + in, out := &in.ScaleUp, &out.ScaleUp + *out = new(HPAScalingRules) + (*in).DeepCopyInto(*out) + } + if in.ScaleDown != nil { + in, out := &in.ScaleDown, &out.ScaleDown + *out = new(HPAScalingRules) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizontalPodAutoscalerBehavior. +func (in *HorizontalPodAutoscalerBehavior) DeepCopy() *HorizontalPodAutoscalerBehavior { + if in == nil { + return nil + } + out := new(HorizontalPodAutoscalerBehavior) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizontalPodAutoscalerCondition) DeepCopyInto(out *HorizontalPodAutoscalerCondition) { *out = *in @@ -171,6 +244,11 @@ func (in *HorizontalPodAutoscalerSpec) DeepCopyInto(out *HorizontalPodAutoscaler (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Behavior != nil { + in, out := &in.Behavior, &out.Behavior + *out = new(HorizontalPodAutoscalerBehavior) + (*in).DeepCopyInto(*out) + } return } diff --git a/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.json b/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.json index 781f1fa501e..bd68059085a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.json +++ b/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.json @@ -138,34 +138,61 @@ } } } - ] + ], + "behavior": { + "scaleUp": { + "stabilizationWindowSeconds": 1761963371, + "selectPolicy": "0矀Kʝ瘴I\\p[ħsĨɆâĺɗŹ倗S", + "policies": [ + { + "type": "嶗U", + "value": -1285424066, + "periodSeconds": -686523310 + } + ] + }, + "scaleDown": { + "stabilizationWindowSeconds": 1206365825, + "selectPolicy": "/ɸɎ R§耶FfBls3!", + "policies": [ + { + "type": "ɾģ毋Ó6dz娝嘚", + "value": 627713162, + "periodSeconds": 1255312175 + } + ] + } + } }, "status": { - "observedGeneration": -6706242512760583856, - "currentReplicas": 1761963371, - "desiredReplicas": 645599318, + "observedGeneration": -7477362499801752548, + "currentReplicas": 267768240, + "desiredReplicas": -127849333, "currentMetrics": [ { - "type": "矀Kʝ瘴I\\p[ħsĨɆâĺɗŹ倗S", + "type": "Ǖɳɷ9Ì崟¿瘦ɖ緕", "object": { "metric": { "name": "46", "selector": { "matchLabels": { - "990-17-hg1-o-p665--4-j8---t6-r7---d--uml-89.n0v-1o-0hv--k6/0_OHz_.B-.-_w_--.8_r_N-.3n-x.-_-_-Nm-_X3.1d_YH3x---5": "s2oy" + "29.-_Z.0_1._hg._o_p665O_4Gj._BXt.O-7___-Y_um-_8r--684._V": "8_...E.-o" }, "matchExpressions": [ { - "key": "rR4_7FA.2", - "operator": "DoesNotExist" + "key": "6-d42--clo90---461v-07r--0---8-30i-uo/9DF", + "operator": "In", + "values": [ + "2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-F9_.5vN5.25aWx.a" + ] } ] } }, "current": { - "value": "861", - "averageValue": "289", - "averageUtilization": 1980459939 + "value": "526", + "averageValue": "860", + "averageUtilization": -126958936 }, "describedObject": { "kind": "53", @@ -178,31 +205,31 @@ "name": "56", "selector": { "matchLabels": { - "7-gg93--5-------g1c-fr4/mQ.GM72_-c-.-.6--3-___t-Z8SUGP.-_.uB-.--.gb_2_-8-----yJY.__-X2": "0_.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-E" + "d5-g-7-7---g88w2k4usz--mj-8o26--26-hs5-jeds4-4tz9x-4.i-l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-99/N_g-..__._____K_g1cXfr4": "ET_..3dCv3j._.-_pP__up.2L_s-o779._-k-N" }, "matchExpressions": [ { - "key": "1q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-83.91z---883d-vj/z.-_Z4.A", - "operator": "NotIn", + "key": "382m88w-pz9d4i-m7---k8235--8--c83-4b-9-1k.1f-53-x1y-8---3----p-pdn--j2---2--82--cj-1-s--op3w/3--Z1Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt.U", + "operator": "In", "values": [ - "G.-_pP__up.2L_s-o779._-k-5___-Qq..csh-3--Z1Tvw39F_C-t" + "y.8_8" ] } ] } }, "current": { - "value": "485", - "averageValue": "638", - "averageUtilization": 1333166203 + "value": "671", + "averageValue": "683", + "averageUtilization": 1008425444 } }, "resource": { - "name": "ɻ;襕ċ桉桃喕", + "name": "Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃", "current": { - "value": "826", - "averageValue": "886", - "averageUtilization": -280562323 + "value": "93", + "averageValue": "183", + "averageUtilization": -392406530 } }, "external": { @@ -210,29 +237,32 @@ "name": "63", "selector": { "matchLabels": { - "Z": "C..7o_x3..-.8-Jp-9-4-Tm.__G-8...__.Q_c8.G.b_91" + "627-23---g-----p8-d5-8-m8i--k0j5-gr-y7nlp97v-0-1y7/2....3_t_-l..-.DG7r-3.----._4__XOnf_ZN.-_--r.E__-8": "K.-miJ4x-_0_5-_7" }, "matchExpressions": [ { - "key": "7pdn--j2---25/I._31-_I-A-_3b6", - "operator": "Exists" + "key": "c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o", + "operator": "In", + "values": [ + "g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7" + ] } ] } }, "current": { - "value": "108", - "averageValue": "909", - "averageUtilization": -1726456869 + "value": "287", + "averageValue": "759", + "averageUtilization": -1175595426 } } } ], "conditions": [ { - "type": "Ƚȿ醏g遧", - "status": "ɸĻo:{柯?B俋¬h`職铳s44矕Ƈ", - "lastTransitionTime": "2039-12-25T06:58:01Z", + "type": "`翾'ųŎ群E牬庘颮6(|ǖû", + "status": "龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E", + "lastTransitionTime": "2682-06-14T06:09:58Z", "reason": "70", "message": "71" } diff --git a/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.pb b/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.pb index 9b892e9e5bdc3cbba04f2e350807e2d240603cbd..7d754d1ba5f7caa297c0156f338ebc6ce3e3e6aa 100644 GIT binary patch delta 1140 zcmXAoe@t6d6vufFhV{lo_B5t8E&_izMbD-0_kG6wA*{s>)2hJ6NX!u_6iQ2*0)>(# zbrA(c*=AElRHoUcHW_o8E5MKG_n$Sdh%=6Fx2DP+b&~>VA~o*xnTnd!6QrWJaIURt<=(_6|L*W5 zIDfqN!_t3yw}0Nw+^m!ikw@waONHs`*RFqc_pJqQPeXW6t&ysUR9IQNxOH_ZpEfpf z#q3;h_T#Ge3)#z}^Xkm4pDLWEem%ch;hJ+eK3)1JxBmUw+^pdI=Hkz(>ZxDG^LB=v zlu&o&0rJ>JrjRQvTKNx(*FL+xTAKZ`FjHF0NF{+R3q;fm>6$=1geevU?4WLxDfR{_ zMlog>x*xTs5XFY}A?gPc5#a<5M*vwIz+f81W72~RDWx6?QK_FiV?aPt0J_3aE9C|N z&5#q|Ho3d~#6WfT!Em(RI_#CiBLlAImEKkgV?faaJ^V7|H2~0Ko=(8v0AHUHslbLB3h~) zDD%Mkxxd%$?@GIFMNXS;6uXU}-3r7Q@BuK0us%+}9pb}?L(&`w z$1Ibp1>~ezuW(UO6^9VQTov?i6W5SR?P!V`V!MB!2RnSbdWlmH7bcUcnkhv3j%uTT zZqxxy>2>mC7toYf$5GnsYjgv5KsQ9dXo;Z@S}2X~vM_>x+N=B{37&A+9_bO}N>pw1 zdb=F&g=Rg8FOK;ej#De@vC!a^B+NYtD$KMQn7ZZsZGLfc?@w1=AqU!emA>);cMgRlN&SnW$D27%7y&+jZ1ACi^aK*)-zr`S{$EV8_!RavKf!d Y;aFJOywg%uRx6tBI?L!rBGxz`1pUn|Noo`Jhl6d%CGMl z(Wm)L=}bOTc+fpvQugldbJ$HVsxOWGQG&5F%I#H zhVOBx4_T%ufT%+xEP@3IO;Z6Bh#COUtwB;}H3ZPpbr}qk#D!iEEf6BT05Q!Y(36cR zN@{=r?V|w1ph?;SM2~6dd6{A8!6EQ22VY(iFn0`^}E=Rm7V_sF_4o#d>PBNFhto1K z#S~LA3^@e7QBR6(@+-uPiTm* z;v)fSiue#ZpeI6cfT+YgN~))cP+0`p1YV$WK*Q0J?YFf5L5PCkK$wE*`Z)I~x3LW& z)u5fZSGk3?q1)lL!6&$K`Q_5Qb>wP({Q0q(&kLEwXOZ)(gL7-6v$mpq9nZcVc9)fH Wycv2}wx^(q*M(|c7u^L_1%Cl;=;}uR diff --git a/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.yaml b/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.yaml index f4dec2fe84f..36d494211b8 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/autoscaling.v2beta2.HorizontalPodAutoscaler.yaml @@ -30,6 +30,21 @@ metadata: selfLink: "5" uid: "7" spec: + behavior: + scaleDown: + policies: + - periodSeconds: 1255312175 + type: ɾģ毋Ó6dz娝嘚 + value: 627713162 + selectPolicy: /ɸɎ R§耶FfBls3! + stabilizationWindowSeconds: 1206365825 + scaleUp: + policies: + - periodSeconds: -686523310 + type: 嶗U + value: -1285424066 + selectPolicy: 0矀Kʝ瘴I\p[ħsĨɆâĺɗŹ倗S + stabilizationWindowSeconds: 1761963371 maxReplicas: -1971381490 metrics: - external: @@ -97,30 +112,32 @@ spec: name: "20" status: conditions: - - lastTransitionTime: "2039-12-25T06:58:01Z" + - lastTransitionTime: "2682-06-14T06:09:58Z" message: "71" reason: "70" - status: ɸĻo:{柯?B俋¬h`職铳s44矕Ƈ - type: Ƚȿ醏g遧 + status: 龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E + type: '`翾''ųŎ群E牬庘颮6(|ǖû' currentMetrics: - external: current: - averageUtilization: -1726456869 - averageValue: "909" - value: "108" + averageUtilization: -1175595426 + averageValue: "759" + value: "287" metric: name: "63" selector: matchExpressions: - - key: 7pdn--j2---25/I._31-_I-A-_3b6 - operator: Exists + - key: c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o + operator: In + values: + - g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7 matchLabels: - Z: C..7o_x3..-.8-Jp-9-4-Tm.__G-8...__.Q_c8.G.b_91 + 627-23---g-----p8-d5-8-m8i--k0j5-gr-y7nlp97v-0-1y7/2....3_t_-l..-.DG7r-3.----._4__XOnf_ZN.-_--r.E__-8: K.-miJ4x-_0_5-_7 object: current: - averageUtilization: 1980459939 - averageValue: "289" - value: "861" + averageUtilization: -126958936 + averageValue: "860" + value: "526" describedObject: apiVersion: "55" kind: "53" @@ -129,32 +146,35 @@ status: name: "46" selector: matchExpressions: - - key: rR4_7FA.2 - operator: DoesNotExist + - key: 6-d42--clo90---461v-07r--0---8-30i-uo/9DF + operator: In + values: + - 2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-F9_.5vN5.25aWx.a matchLabels: - 990-17-hg1-o-p665--4-j8---t6-r7---d--uml-89.n0v-1o-0hv--k6/0_OHz_.B-.-_w_--.8_r_N-.3n-x.-_-_-Nm-_X3.1d_YH3x---5: s2oy + 29.-_Z.0_1._hg._o_p665O_4Gj._BXt.O-7___-Y_um-_8r--684._V: 8_...E.-o pods: current: - averageUtilization: 1333166203 - averageValue: "638" - value: "485" + averageUtilization: 1008425444 + averageValue: "683" + value: "671" metric: name: "56" selector: matchExpressions: - - key: 1q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-83.91z---883d-vj/z.-_Z4.A - operator: NotIn + - key: 382m88w-pz9d4i-m7---k8235--8--c83-4b-9-1k.1f-53-x1y-8---3----p-pdn--j2---2--82--cj-1-s--op3w/3--Z1Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt.U + operator: In values: - - G.-_pP__up.2L_s-o779._-k-5___-Qq..csh-3--Z1Tvw39F_C-t + - y.8_8 matchLabels: - 7-gg93--5-------g1c-fr4/mQ.GM72_-c-.-.6--3-___t-Z8SUGP.-_.uB-.--.gb_2_-8-----yJY.__-X2: 0_.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-E + ? d5-g-7-7---g88w2k4usz--mj-8o26--26-hs5-jeds4-4tz9x-4.i-l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-99/N_g-..__._____K_g1cXfr4 + : ET_..3dCv3j._.-_pP__up.2L_s-o779._-k-N resource: current: - averageUtilization: -280562323 - averageValue: "886" - value: "826" - name: ɻ;襕ċ桉桃喕 - type: 矀Kʝ瘴I\p[ħsĨɆâĺɗŹ倗S - currentReplicas: 1761963371 - desiredReplicas: 645599318 - observedGeneration: -6706242512760583856 + averageUtilization: -392406530 + averageValue: "183" + value: "93" + name: Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃 + type: Ǖɳɷ9Ì崟¿瘦ɖ緕 + currentReplicas: 267768240 + desiredReplicas: -127849333 + observedGeneration: -7477362499801752548 From 8ab226263a18fa16afdb6fd14da63544095a8937 Mon Sep 17 00:00:00 2001 From: Arjun Naik Date: Tue, 10 Dec 2019 18:09:08 +0100 Subject: [PATCH 5/5] Adds tests Signed-off-by: Arjun Naik --- pkg/api/testing/defaulting_test.go | 2 + pkg/apis/autoscaling/fuzzer/fuzzer.go | 37 + pkg/apis/autoscaling/v2beta2/BUILD | 13 +- pkg/apis/autoscaling/v2beta2/defaults_test.go | 231 ++++ .../autoscaling/validation/validation_test.go | 370 +++++++ pkg/controller/podautoscaler/BUILD | 2 + .../podautoscaler/horizontal_test.go | 998 +++++++++++++++++- .../podautoscaler/legacy_horizontal_test.go | 2 +- .../pkg/describe/versioned/describe_test.go | 70 ++ 9 files changed, 1683 insertions(+), 42 deletions(-) create mode 100644 pkg/apis/autoscaling/v2beta2/defaults_test.go diff --git a/pkg/api/testing/defaulting_test.go b/pkg/api/testing/defaulting_test.go index b9367217567..810b7563f1d 100644 --- a/pkg/api/testing/defaulting_test.go +++ b/pkg/api/testing/defaulting_test.go @@ -79,6 +79,8 @@ func TestDefaulting(t *testing.T) { {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {}, {Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}: {}, {Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}: {}, + {Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscaler"}: {}, + {Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscalerList"}: {}, {Group: "batch", Version: "v1", Kind: "Job"}: {}, {Group: "batch", Version: "v1", Kind: "JobList"}: {}, {Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {}, diff --git a/pkg/apis/autoscaling/fuzzer/fuzzer.go b/pkg/apis/autoscaling/fuzzer/fuzzer.go index 14e14a1b55e..d8956bb185b 100644 --- a/pkg/apis/autoscaling/fuzzer/fuzzer.go +++ b/pkg/apis/autoscaling/fuzzer/fuzzer.go @@ -90,6 +90,43 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { }, }, } + stabilizationWindow := int32(c.RandUint64()) + maxPolicy := autoscaling.MaxPolicySelect + minPolicy := autoscaling.MinPolicySelect + s.Behavior = &autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindow, + SelectPolicy: &maxPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: int32(c.RandUint64()), + PeriodSeconds: int32(c.RandUint64()), + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: int32(c.RandUint64()), + PeriodSeconds: int32(c.RandUint64()), + }, + }, + }, + ScaleDown: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: &stabilizationWindow, + SelectPolicy: &minPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: int32(c.RandUint64()), + PeriodSeconds: int32(c.RandUint64()), + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: int32(c.RandUint64()), + PeriodSeconds: int32(c.RandUint64()), + }, + }, + }, + } }, func(s *autoscaling.HorizontalPodAutoscalerStatus, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again diff --git a/pkg/apis/autoscaling/v2beta2/BUILD b/pkg/apis/autoscaling/v2beta2/BUILD index 6c7d5eb4bbc..07609190dbe 100644 --- a/pkg/apis/autoscaling/v2beta2/BUILD +++ b/pkg/apis/autoscaling/v2beta2/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -37,3 +37,14 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["defaults_test.go"], + embed = [":go_default_library"], + deps = [ + "//staging/src/k8s.io/api/autoscaling/v2beta2:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", + ], +) diff --git a/pkg/apis/autoscaling/v2beta2/defaults_test.go b/pkg/apis/autoscaling/v2beta2/defaults_test.go new file mode 100644 index 00000000000..fcdac48da01 --- /dev/null +++ b/pkg/apis/autoscaling/v2beta2/defaults_test.go @@ -0,0 +1,231 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta2_test + +import ( + "testing" + + autoscalingv2 "k8s.io/api/autoscaling/v2beta2" + . "k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2" + utilpointer "k8s.io/utils/pointer" + + "github.com/stretchr/testify/assert" +) + +func TestGenerateScaleDownRules(t *testing.T) { + type TestCase struct { + rateDownPods int32 + rateDownPodsPeriodSeconds int32 + rateDownPercent int32 + rateDownPercentPeriodSeconds int32 + stabilizationSeconds *int32 + selectPolicy *autoscalingv2.ScalingPolicySelect + + expectedPolicies []autoscalingv2.HPAScalingPolicy + expectedStabilization *int32 + expectedSelectPolicy string + annotation string + } + maxPolicy := autoscalingv2.MaxPolicySelect + minPolicy := autoscalingv2.MinPolicySelect + tests := []TestCase{ + { + annotation: "Default values", + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PercentScalingPolicy, Value: 100, PeriodSeconds: 15}, + }, + expectedStabilization: nil, + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "All parameters are specified", + rateDownPods: 1, + rateDownPodsPeriodSeconds: 2, + rateDownPercent: 3, + rateDownPercentPeriodSeconds: 4, + stabilizationSeconds: utilpointer.Int32Ptr(25), + selectPolicy: &maxPolicy, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 1, PeriodSeconds: 2}, + {Type: autoscalingv2.PercentScalingPolicy, Value: 3, PeriodSeconds: 4}, + }, + expectedStabilization: utilpointer.Int32Ptr(25), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "Percent policy is specified", + rateDownPercent: 1, + rateDownPercentPeriodSeconds: 2, + selectPolicy: &minPolicy, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PercentScalingPolicy, Value: 1, PeriodSeconds: 2}, + }, + expectedStabilization: nil, + expectedSelectPolicy: string(autoscalingv2.MinPolicySelect), + }, + { + annotation: "Pods policy is specified", + rateDownPods: 3, + rateDownPodsPeriodSeconds: 4, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 3, PeriodSeconds: 4}, + }, + expectedStabilization: nil, + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + } + for _, tc := range tests { + t.Run(tc.annotation, func(t *testing.T) { + scaleDownRules := &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: tc.stabilizationSeconds, + SelectPolicy: tc.selectPolicy, + } + if tc.rateDownPods != 0 || tc.rateDownPodsPeriodSeconds != 0 { + scaleDownRules.Policies = append(scaleDownRules.Policies, autoscalingv2.HPAScalingPolicy{ + Type: autoscalingv2.PodsScalingPolicy, Value: tc.rateDownPods, PeriodSeconds: tc.rateDownPodsPeriodSeconds, + }) + } + if tc.rateDownPercent != 0 || tc.rateDownPercentPeriodSeconds != 0 { + scaleDownRules.Policies = append(scaleDownRules.Policies, autoscalingv2.HPAScalingPolicy{ + Type: autoscalingv2.PercentScalingPolicy, Value: tc.rateDownPercent, PeriodSeconds: tc.rateDownPercentPeriodSeconds, + }) + } + down := GenerateHPAScaleDownRules(scaleDownRules) + assert.EqualValues(t, tc.expectedPolicies, down.Policies) + if tc.expectedStabilization != nil { + assert.Equal(t, *tc.expectedStabilization, *down.StabilizationWindowSeconds) + } else { + assert.Equal(t, tc.expectedStabilization, down.StabilizationWindowSeconds) + } + assert.Equal(t, autoscalingv2.ScalingPolicySelect(tc.expectedSelectPolicy), *down.SelectPolicy) + }) + } +} + +func TestGenerateScaleUpRules(t *testing.T) { + type TestCase struct { + rateUpPods int32 + rateUpPodsPeriodSeconds int32 + rateUpPercent int32 + rateUpPercentPeriodSeconds int32 + stabilizationSeconds *int32 + selectPolicy *autoscalingv2.ScalingPolicySelect + + expectedPolicies []autoscalingv2.HPAScalingPolicy + expectedStabilization *int32 + expectedSelectPolicy string + annotation string + } + maxPolicy := autoscalingv2.MaxPolicySelect + minPolicy := autoscalingv2.MinPolicySelect + tests := []TestCase{ + { + annotation: "Default values", + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 4, PeriodSeconds: 15}, + {Type: autoscalingv2.PercentScalingPolicy, Value: 100, PeriodSeconds: 15}, + }, + expectedStabilization: utilpointer.Int32Ptr(0), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "All parameters are specified", + rateUpPods: 1, + rateUpPodsPeriodSeconds: 2, + rateUpPercent: 3, + rateUpPercentPeriodSeconds: 4, + stabilizationSeconds: utilpointer.Int32Ptr(25), + selectPolicy: &maxPolicy, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 1, PeriodSeconds: 2}, + {Type: autoscalingv2.PercentScalingPolicy, Value: 3, PeriodSeconds: 4}, + }, + expectedStabilization: utilpointer.Int32Ptr(25), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "Pod policy is specified", + rateUpPods: 1, + rateUpPodsPeriodSeconds: 2, + selectPolicy: &minPolicy, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 1, PeriodSeconds: 2}, + }, + expectedStabilization: utilpointer.Int32Ptr(0), + expectedSelectPolicy: string(autoscalingv2.MinPolicySelect), + }, + { + annotation: "Percent policy is specified", + rateUpPercent: 7, + rateUpPercentPeriodSeconds: 10, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PercentScalingPolicy, Value: 7, PeriodSeconds: 10}, + }, + expectedStabilization: utilpointer.Int32Ptr(0), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "Pod policy and stabilization window are specified", + rateUpPodsPeriodSeconds: 2, + stabilizationSeconds: utilpointer.Int32Ptr(25), + rateUpPods: 4, + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PodsScalingPolicy, Value: 4, PeriodSeconds: 2}, + }, + expectedStabilization: utilpointer.Int32Ptr(25), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + { + annotation: "Percent policy and stabilization window are specified", + rateUpPercent: 7, + rateUpPercentPeriodSeconds: 60, + stabilizationSeconds: utilpointer.Int32Ptr(25), + expectedPolicies: []autoscalingv2.HPAScalingPolicy{ + {Type: autoscalingv2.PercentScalingPolicy, Value: 7, PeriodSeconds: 60}, + }, + expectedStabilization: utilpointer.Int32Ptr(25), + expectedSelectPolicy: string(autoscalingv2.MaxPolicySelect), + }, + } + for _, tc := range tests { + t.Run(tc.annotation, func(t *testing.T) { + scaleUpRules := &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: tc.stabilizationSeconds, + SelectPolicy: tc.selectPolicy, + } + if tc.rateUpPods != 0 || tc.rateUpPodsPeriodSeconds != 0 { + scaleUpRules.Policies = append(scaleUpRules.Policies, autoscalingv2.HPAScalingPolicy{ + Type: autoscalingv2.PodsScalingPolicy, Value: tc.rateUpPods, PeriodSeconds: tc.rateUpPodsPeriodSeconds, + }) + } + if tc.rateUpPercent != 0 || tc.rateUpPercentPeriodSeconds != 0 { + scaleUpRules.Policies = append(scaleUpRules.Policies, autoscalingv2.HPAScalingPolicy{ + Type: autoscalingv2.PercentScalingPolicy, Value: tc.rateUpPercent, PeriodSeconds: tc.rateUpPercentPeriodSeconds, + }) + } + up := GenerateHPAScaleUpRules(scaleUpRules) + assert.Equal(t, tc.expectedPolicies, up.Policies) + if tc.expectedStabilization != nil { + assert.Equal(t, *tc.expectedStabilization, *up.StabilizationWindowSeconds) + } else { + assert.Equal(t, tc.expectedStabilization, up.StabilizationWindowSeconds) + } + + assert.Equal(t, autoscalingv2.ScalingPolicySelect(tc.expectedSelectPolicy), *up.SelectPolicy) + }) + } +} diff --git a/pkg/apis/autoscaling/validation/validation_test.go b/pkg/apis/autoscaling/validation/validation_test.go index 1e54bc46f04..6a2884268bb 100644 --- a/pkg/apis/autoscaling/validation/validation_test.go +++ b/pkg/apis/autoscaling/validation/validation_test.go @@ -94,6 +94,376 @@ func TestValidateScale(t *testing.T) { } } +func TestValidateBehavior(t *testing.T) { + maxPolicy := autoscaling.MaxPolicySelect + minPolicy := autoscaling.MinPolicySelect + disabledPolicy := autoscaling.DisabledPolicySelect + incorrectPolicy := autoscaling.ScalingPolicySelect("incorrect") + simplePoliciesList := []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PercentScalingPolicy, + Value: 10, + PeriodSeconds: 1, + }, + { + Type: autoscaling.PodsScalingPolicy, + Value: 1, + PeriodSeconds: 1800, + }, + } + successCases := []autoscaling.HorizontalPodAutoscalerBehavior{ + { + ScaleUp: nil, + ScaleDown: nil, + }, + { + ScaleUp: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(3600), + SelectPolicy: &minPolicy, + Policies: simplePoliciesList, + }, + ScaleDown: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(0), + SelectPolicy: &disabledPolicy, + Policies: simplePoliciesList, + }, + }, + { + ScaleUp: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(120), + SelectPolicy: &maxPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 1, + PeriodSeconds: 2, + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: 3, + PeriodSeconds: 4, + }, + { + Type: autoscaling.PodsScalingPolicy, + Value: 5, + PeriodSeconds: 6, + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + ScaleDown: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(120), + SelectPolicy: &maxPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 1, + PeriodSeconds: 2, + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: 3, + PeriodSeconds: 4, + }, + { + Type: autoscaling.PodsScalingPolicy, + Value: 5, + PeriodSeconds: 6, + }, + { + Type: autoscaling.PercentScalingPolicy, + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + }, + } + for _, behavior := range successCases { + hpa := prepareHPAWithBehavior(behavior) + if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + errorCases := []struct { + behavior autoscaling.HorizontalPodAutoscalerBehavior + msg string + }{ + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + SelectPolicy: &minPolicy, + }, + }, + msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(3601), + SelectPolicy: &minPolicy, + Policies: simplePoliciesList, + }, + }, + msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 7, + PeriodSeconds: 1801, + }, + }, + }, + }, + msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + SelectPolicy: &incorrectPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.HPAScalingPolicyType("hm"), + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 8, + }, + }, + }, + }, + msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: -1, + Value: 1, + }, + }, + }, + }, + msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: 1, + Value: -1, + }, + }, + }, + }, + msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + SelectPolicy: &minPolicy, + }, + }, + msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(3601), + SelectPolicy: &minPolicy, + Policies: simplePoliciesList, + }, + }, + msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PercentScalingPolicy, + Value: 7, + PeriodSeconds: 1801, + }, + }, + }, + }, + msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + SelectPolicy: &incorrectPolicy, + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.HPAScalingPolicyType("hm"), + Value: 7, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + Value: 8, + }, + }, + }, + }, + msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: 8, + }, + }, + }, + }, + msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: -1, + Value: 1, + }, + }, + }, + }, + msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", + }, + { + behavior: autoscaling.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscaling.HPAScalingRules{ + Policies: []autoscaling.HPAScalingPolicy{ + { + Type: autoscaling.PodsScalingPolicy, + PeriodSeconds: 1, + Value: -1, + }, + }, + }, + }, + msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero", + }, + } + for _, c := range errorCases { + hpa := prepareHPAWithBehavior(c.behavior) + if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) == 0 { + t.Errorf("expected failure for %s", c.msg) + } else if !strings.Contains(errs[0].Error(), c.msg) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) + } + } +} + +func prepareHPAWithBehavior(b autoscaling.HorizontalPodAutoscalerBehavior) autoscaling.HorizontalPodAutoscaler { + return autoscaling.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myautoscaler", + Namespace: metav1.NamespaceDefault, + }, + Spec: autoscaling.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscaling.CrossVersionObjectReference{ + Kind: "ReplicationController", + Name: "myrc", + }, + MinReplicas: utilpointer.Int32Ptr(1), + MaxReplicas: 5, + Metrics: []autoscaling.MetricSpec{ + { + Type: autoscaling.ResourceMetricSourceType, + Resource: &autoscaling.ResourceMetricSource{ + Name: api.ResourceCPU, + Target: autoscaling.MetricTarget{ + Type: autoscaling.UtilizationMetricType, + AverageUtilization: utilpointer.Int32Ptr(70), + }, + }, + }, + }, + Behavior: &b, + }, + } +} + func TestValidateHorizontalPodAutoscaler(t *testing.T) { metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") if err != nil { diff --git a/pkg/controller/podautoscaler/BUILD b/pkg/controller/podautoscaler/BUILD index c6ad0ce212c..ba5b8fa2a6f 100644 --- a/pkg/controller/podautoscaler/BUILD +++ b/pkg/controller/podautoscaler/BUILD @@ -58,6 +58,7 @@ go_test( "//pkg/apis/apps/install:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/autoscaling/install:go_default_library", + "//pkg/apis/autoscaling/v2beta2:go_default_library", "//pkg/apis/core/install:go_default_library", "//pkg/controller:go_default_library", "//pkg/controller/podautoscaler/metrics:go_default_library", @@ -89,6 +90,7 @@ go_test( "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/require:go_default_library", "//vendor/k8s.io/heapster/metrics/api/v1/types:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", ], ) diff --git a/pkg/controller/podautoscaler/horizontal_test.go b/pkg/controller/podautoscaler/horizontal_test.go index b588b569ab4..877a2f0340c 100644 --- a/pkg/controller/podautoscaler/horizontal_test.go +++ b/pkg/controller/podautoscaler/horizontal_test.go @@ -40,6 +40,7 @@ import ( core "k8s.io/client-go/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/autoscaling" + autoscalingapiv2beta2 "k8s.io/kubernetes/pkg/apis/autoscaling/v2beta2" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics" cmapi "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" @@ -48,6 +49,7 @@ import ( metricsfake "k8s.io/metrics/pkg/client/clientset/versioned/fake" cmfake "k8s.io/metrics/pkg/client/custom_metrics/fake" emfake "k8s.io/metrics/pkg/client/external_metrics/fake" + utilpointer "k8s.io/utils/pointer" "github.com/stretchr/testify/assert" @@ -55,6 +57,15 @@ import ( _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" ) +// From now on, the HPA controller does have history in it (scaleUpEvents, scaleDownEvents) +// Hence the second HPA controller reconcile cycle might return different result (comparing with the first run). +// Current test infrastructure has a race condition, when several reconcile cycles will be performed +// while it should be stopped right after the first one. And the second will raise an exception +// because of different result. + +// This comment has more info: https://github.com/kubernetes/kubernetes/pull/74525#issuecomment-502653106 +// We need to rework this infrastructure: https://github.com/kubernetes/kubernetes/issues/79222 + var statusOk = []autoscalingv2.HorizontalPodAutoscalerCondition{ {Type: autoscalingv2.AbleToScale, Status: v1.ConditionTrue, Reason: "SucceededRescale"}, {Type: autoscalingv2.ScalingActive, Status: v1.ConditionTrue, Reason: "ValidMetricFound"}, @@ -92,10 +103,13 @@ type fakeResource struct { type testCase struct { sync.Mutex - minReplicas int32 - maxReplicas int32 - specReplicas int32 - statusReplicas int32 + minReplicas int32 + maxReplicas int32 + specReplicas int32 + statusReplicas int32 + initialReplicas int32 + scaleUpRules *autoscalingv2.HPAScalingRules + scaleDownRules *autoscalingv2.HPAScalingRules // CPU target utilization as a percentage of the requested resources. CPUTarget int32 @@ -185,31 +199,40 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa fakeClient.AddReactor("list", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() + var behavior *autoscalingv2.HorizontalPodAutoscalerBehavior + if tc.scaleUpRules != nil || tc.scaleDownRules != nil { + behavior = &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: tc.scaleUpRules, + ScaleDown: tc.scaleDownRules, + } + } + hpa := autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: hpaName, + Namespace: namespace, + SelfLink: "experimental/v1/namespaces/" + namespace + "/horizontalpodautoscalers/" + hpaName, + }, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + Kind: tc.resource.kind, + Name: tc.resource.name, + APIVersion: tc.resource.apiVersion, + }, + MinReplicas: &tc.minReplicas, + MaxReplicas: tc.maxReplicas, + Behavior: behavior, + }, + Status: autoscalingv2.HorizontalPodAutoscalerStatus{ + CurrentReplicas: tc.specReplicas, + DesiredReplicas: tc.specReplicas, + LastScaleTime: tc.lastScaleTime, + }, + } + // Initialize default values + autoscalingapiv2beta2.SetDefaults_HorizontalPodAutoscalerBehavior(&hpa) obj := &autoscalingv2.HorizontalPodAutoscalerList{ - Items: []autoscalingv2.HorizontalPodAutoscaler{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: hpaName, - Namespace: namespace, - SelfLink: "experimental/v1/namespaces/" + namespace + "/horizontalpodautoscalers/" + hpaName, - }, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ - Kind: tc.resource.kind, - Name: tc.resource.name, - APIVersion: tc.resource.apiVersion, - }, - MinReplicas: &tc.minReplicas, - MaxReplicas: tc.maxReplicas, - }, - Status: autoscalingv2.HorizontalPodAutoscalerStatus{ - CurrentReplicas: tc.specReplicas, - DesiredReplicas: tc.specReplicas, - LastScaleTime: tc.lastScaleTime, - }, - }, - }, + Items: []autoscalingv2.HorizontalPodAutoscaler{hpa}, } if tc.CPUTarget > 0 { @@ -678,7 +701,7 @@ func (tc *testCase) setupController(t *testing.T) (*HorizontalController, inform metricsClient, informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(), informerFactory.Core().V1().Pods(), - controller.NoResyncPeriodFunc(), + 100*time.Millisecond, // we need non-zero resync period to avoid race conditions defaultDownscalestabilizationWindow, defaultTestingTolerance, defaultTestingCpuInitializationPeriod, @@ -1246,6 +1269,24 @@ func TestScaleDown(t *testing.T) { tc.runTest(t) } +func TestScaleDownWithScalingRules(t *testing.T) { + tc := testCase{ + minReplicas: 2, + maxReplicas: 6, + scaleUpRules: generateScalingRules(0, 0, 100, 15, 30), + specReplicas: 5, + statusReplicas: 5, + expectedDesiredReplicas: 3, + CPUTarget: 50, + verifyCPUCurrent: true, + reportedLevels: []uint64{100, 300, 500, 250, 250}, + reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, + useMetricsAPI: true, + recommendations: []timestampedRecommendation{}, + } + tc.runTest(t) +} + func TestScaleUpOneMetricInvalid(t *testing.T) { tc := testCase{ minReplicas: 2, @@ -2143,6 +2184,8 @@ func TestUpscaleCap(t *testing.T) { maxReplicas: 100, specReplicas: 3, statusReplicas: 3, + scaleUpRules: generateScalingRules(0, 0, 700, 60, 0), + initialReplicas: 3, expectedDesiredReplicas: 24, CPUTarget: 10, reportedLevels: []uint64{100, 200, 300}, @@ -2159,10 +2202,12 @@ func TestUpscaleCap(t *testing.T) { func TestUpscaleCapGreaterThanMaxReplicas(t *testing.T) { tc := testCase{ - minReplicas: 1, - maxReplicas: 20, - specReplicas: 3, - statusReplicas: 3, + minReplicas: 1, + maxReplicas: 20, + specReplicas: 3, + statusReplicas: 3, + scaleUpRules: generateScalingRules(0, 0, 700, 60, 0), + initialReplicas: 3, // expectedDesiredReplicas would be 24 without maxReplicas expectedDesiredReplicas: 20, CPUTarget: 10, @@ -2914,7 +2959,7 @@ func TestConvertDesiredReplicasWithRules(t *testing.T) { hpaMaxReplicas: 10, expectedConvertedDesiredReplicas: 0, expectedCondition: "DesiredWithinRange", - annotation: "prenormalized desired replicas within range", + annotation: "prenormalized desired zeroed replicas within range", }, { currentReplicas: 20, @@ -2937,15 +2982,60 @@ func TestConvertDesiredReplicasWithRules(t *testing.T) { } for _, ctc := range conversionTestCases { - actualConvertedDesiredReplicas, actualCondition, _ := convertDesiredReplicasWithRules( - ctc.currentReplicas, ctc.expectedDesiredReplicas, ctc.hpaMinReplicas, ctc.hpaMaxReplicas, - ) + t.Run(ctc.annotation, func(t *testing.T) { + actualConvertedDesiredReplicas, actualCondition, _ := convertDesiredReplicasWithRules( + ctc.currentReplicas, ctc.expectedDesiredReplicas, ctc.hpaMinReplicas, ctc.hpaMaxReplicas, + ) - assert.Equal(t, ctc.expectedConvertedDesiredReplicas, actualConvertedDesiredReplicas, ctc.annotation) - assert.Equal(t, ctc.expectedCondition, actualCondition, ctc.annotation) + assert.Equal(t, ctc.expectedConvertedDesiredReplicas, actualConvertedDesiredReplicas, ctc.annotation) + assert.Equal(t, ctc.expectedCondition, actualCondition, ctc.annotation) + }) } } +func generateScalingRules(pods, podsPeriod, percent, percentPeriod, stabilizationWindow int32) *autoscalingv2.HPAScalingRules { + policy := autoscalingv2.MaxPolicySelect + directionBehavior := autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(stabilizationWindow), + SelectPolicy: &policy, + } + if pods != 0 { + directionBehavior.Policies = append(directionBehavior.Policies, + autoscalingv2.HPAScalingPolicy{Type: autoscalingv2.PodsScalingPolicy, Value: pods, PeriodSeconds: podsPeriod}) + } + if percent != 0 { + directionBehavior.Policies = append(directionBehavior.Policies, + autoscalingv2.HPAScalingPolicy{Type: autoscalingv2.PercentScalingPolicy, Value: percent, PeriodSeconds: percentPeriod}) + } + return &directionBehavior +} + +// generateEventsUniformDistribution generates events that uniformly spread in the time window +// time.Now()-periodSeconds ; time.Now() +// It split the time window into several segments (by the number of events) and put the event in the center of the segment +// it is needed if you want to create events for several policies (to check how "outdated" flag is set). +// E.g. generateEventsUniformDistribution([]int{1,2,3,4}, 120) will spread events uniformly for the last 120 seconds: +// +// 1 2 3 4 +// ----------------------------------------------- +// ^ ^ ^ ^ ^ +// -120s -90s -60s -30s now() +// And we can safely have two different stabilizationWindows: +// - 60s (guaranteed to have last half of events) +// - 120s (guaranteed to have all events) +func generateEventsUniformDistribution(rawEvents []int, periodSeconds int) []timestampedScaleEvent { + events := make([]timestampedScaleEvent, len(rawEvents)) + segmentDuration := float64(periodSeconds) / float64(len(rawEvents)) + for idx, event := range rawEvents { + segmentBoundary := time.Duration(float64(periodSeconds) - segmentDuration*float64(idx+1) + segmentDuration/float64(2)) + events[idx] = timestampedScaleEvent{ + replicaChange: int32(event), + timestamp: time.Now().Add(-time.Second * segmentBoundary), + } + } + return events +} + func TestNormalizeDesiredReplicas(t *testing.T) { tests := []struct { name string @@ -3027,6 +3117,836 @@ func TestNormalizeDesiredReplicas(t *testing.T) { } } +func TestScalingWithRules(t *testing.T) { + type TestCase struct { + name string + key string + // controller arguments + scaleUpEvents []timestampedScaleEvent + scaleDownEvents []timestampedScaleEvent + // HPA Spec arguments + specMinReplicas int32 + specMaxReplicas int32 + scaleUpRules *autoscalingv2.HPAScalingRules + scaleDownRules *autoscalingv2.HPAScalingRules + // external world state + currentReplicas int32 + prenormalizedDesiredReplicas int32 + // test expected result + expectedReplicas int32 + expectedCondition string + + testThis bool + } + + tests := []TestCase{ + { + currentReplicas: 5, + prenormalizedDesiredReplicas: 7, + specMinReplicas: 3, + specMaxReplicas: 8, + expectedReplicas: 7, + expectedCondition: "DesiredWithinRange", + name: "prenormalized desired replicas within range", + }, + { + currentReplicas: 3, + prenormalizedDesiredReplicas: 1, + specMinReplicas: 2, + specMaxReplicas: 8, + expectedReplicas: 2, + expectedCondition: "TooFewReplicas", + name: "prenormalized desired replicas < minReplicas", + }, + { + currentReplicas: 1, + prenormalizedDesiredReplicas: 0, + specMinReplicas: 0, + specMaxReplicas: 10, + expectedReplicas: 0, + expectedCondition: "DesiredWithinRange", + name: "prenormalized desired replicas within range when minReplicas is 0", + }, + { + currentReplicas: 20, + prenormalizedDesiredReplicas: 1000, + specMinReplicas: 1, + specMaxReplicas: 10, + expectedReplicas: 10, + expectedCondition: "TooManyReplicas", + name: "maxReplicas is the limit because maxReplicas < scaleUpLimit", + }, + { + currentReplicas: 100, + prenormalizedDesiredReplicas: 1000, + specMinReplicas: 100, + specMaxReplicas: 150, + expectedReplicas: 150, + expectedCondition: "TooManyReplicas", + name: "desired replica count is more than the maximum replica count", + }, + { + currentReplicas: 3, + prenormalizedDesiredReplicas: 1000, + specMinReplicas: 1, + specMaxReplicas: 2000, + expectedReplicas: 4, + expectedCondition: "ScaleUpLimit", + scaleUpRules: generateScalingRules(0, 0, 1, 60, 0), + name: "scaleUpLimit is the limit because scaleUpLimit < maxReplicas with user policies", + }, + { + currentReplicas: 1000, + prenormalizedDesiredReplicas: 3, + specMinReplicas: 3, + specMaxReplicas: 2000, + scaleDownRules: generateScalingRules(20, 60, 0, 0, 0), + expectedReplicas: 980, + expectedCondition: "ScaleDownLimit", + name: "scaleDownLimit is the limit because scaleDownLimit > minReplicas with user defined policies", + testThis: true, + }, + // ScaleUp without PeriodSeconds usage + { + name: "scaleUp with default behavior", + specMinReplicas: 1, + specMaxReplicas: 1000, + currentReplicas: 10, + prenormalizedDesiredReplicas: 50, + expectedReplicas: 20, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with pods policy larger than percent policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(100, 60, 100, 60, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 110, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with percent policy larger than pods policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(2, 60, 100, 60, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 20, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with spec MaxReplicas limitation with large pod policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(100, 60, 0, 0, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 50, + expectedReplicas: 50, + expectedCondition: "DesiredWithinRange", + }, + { + name: "scaleUp with spec MaxReplicas limitation with large percent policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(10000, 60, 0, 0, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 50, + expectedReplicas: 50, + expectedCondition: "DesiredWithinRange", + }, + { + name: "scaleUp with pod policy limitation", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(30, 60, 0, 0, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 50, + expectedReplicas: 40, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with percent policy limitation", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(0, 0, 200, 60, 0), + currentReplicas: 10, + prenormalizedDesiredReplicas: 50, + expectedReplicas: 30, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleDown with percent policy larger than pod policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(20, 60, 1, 60, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 80, + expectedCondition: "ScaleDownLimit", + }, + { + name: "scaleDown with pod policy larger than percent policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(2, 60, 1, 60, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 98, + expectedCondition: "ScaleDownLimit", + }, + { + name: "scaleDown with spec MinReplicas=nil limitation with large pod policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(100, 60, 0, 0, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 1, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with spec MinReplicas limitation with large pod policy", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(100, 60, 0, 0, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 1, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with spec MinReplicas limitation with large percent policy", + specMinReplicas: 5, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 100, 60, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 5, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with pod policy limitation", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(5, 60, 0, 0, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 5, + expectedCondition: "ScaleDownLimit", + }, + { + name: "scaleDown with percent policy limitation", + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 50, 60, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, + expectedCondition: "DesiredWithinRange", + }, + { + name: "scaleUp with spec MaxReplicas limitation with large pod policy and events", + scaleUpEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 200, + scaleUpRules: generateScalingRules(300, 60, 0, 0, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 200, // 200 < 100 - 15 + 300 + expectedCondition: "TooManyReplicas", + }, + { + name: "scaleUp with spec MaxReplicas limitation with large percent policy and events", + scaleUpEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 200, + scaleUpRules: generateScalingRules(0, 0, 10000, 60, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 200, + expectedCondition: "TooManyReplicas", + }, + { + // corner case for calculating the scaleUpLimit, when we changed pod policy after a lot of scaleUp events + // in this case we shouldn't allow scale up, though, the naive formula will suggest that scaleUplimit is less then CurrentReplicas (100-15+5 < 100) + name: "scaleUp with currentReplicas limitation with rate.PeriodSeconds with a lot of recent scale up events", + scaleUpEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(5, 120, 0, 0, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 100, // 120 seconds ago we had (100 - 15) replicas, now the rate.Pods = 5, + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with pod policy and previous scale up events", + scaleUpEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(150, 120, 0, 0, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 235, // 100 - 15 + 150 + expectedCondition: "ScaleUpLimit", + }, + { + name: "scaleUp with percent policy and previous scale up events", + scaleUpEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(0, 0, 200, 120, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 500, + expectedReplicas: 255, // (100 - 15) + 200% + expectedCondition: "ScaleUpLimit", + }, + // ScaleDown with PeriodSeconds usage + { + name: "scaleDown with default policy and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + currentReplicas: 10, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, // without scaleDown rate limitations the PeriodSeconds does not influence anything + expectedCondition: "DesiredWithinRange", + }, + { + name: "scaleDown with spec MinReplicas=nil limitation with large pod policy and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(115, 120, 0, 0, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 1, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with spec MinReplicas limitation with large pod policy and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 5, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(130, 120, 0, 0, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 5, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with spec MinReplicas limitation with large percent policy and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 5, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 100, 120, 300), // 100% removal - is always to 0 => limited by MinReplicas + currentReplicas: 100, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 5, + expectedCondition: "TooFewReplicas", + }, + { + name: "scaleDown with pod policy limitation and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{1, 5, 9}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(5, 120, 0, 0, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 2, + expectedReplicas: 100, // 100 + 15 - 5 + expectedCondition: "ScaleDownLimit", + }, + { + name: "scaleDown with percent policy limitation and previous events", + scaleDownEvents: generateEventsUniformDistribution([]int{2, 4, 6}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 50, 120, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 56, // (100 + 12) - 50% + expectedCondition: "ScaleDownLimit", + }, + { + // corner case for calculating the scaleDownLimit, when we changed pod or percent policy after a lot of scaleDown events + // in this case we shouldn't allow scale down, though, the naive formula will suggest that scaleDownlimit is more then CurrentReplicas (100+30-10% > 100) + name: "scaleDown with previous events preventing further scale down", + scaleDownEvents: generateEventsUniformDistribution([]int{10, 10, 10}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 10, 120, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 0, + expectedReplicas: 100, // (100 + 30) - 10% = 117 is more then 100 (currentReplicas), keep 100 + expectedCondition: "ScaleDownLimit", + }, + { + // corner case, the same as above, but calculation shows that we should go below zero + name: "scaleDown with with previous events still allowing more scale down", + scaleDownEvents: generateEventsUniformDistribution([]int{10, 10, 10}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(0, 0, 1000, 120, 300), + currentReplicas: 10, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, // (10 + 30) - 1000% = -360 is less than 0 and less then 5 (desired by metrics), set 5 + expectedCondition: "DesiredWithinRange", + }, + { + name: "check 'outdated' flag for events for one behavior for up", + scaleUpEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(1000, 60, 0, 0, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 200, + expectedReplicas: 200, + expectedCondition: "DesiredWithinRange", + }, + { + name: "check that events were not marked 'outdated' for two different policies in the behavior for up", + scaleUpEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(1000, 120, 100, 60, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 200, + expectedReplicas: 200, + expectedCondition: "DesiredWithinRange", + }, + { + name: "check that events were marked 'outdated' for two different policies in the behavior for up", + scaleUpEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleUpRules: generateScalingRules(1000, 30, 100, 60, 0), + currentReplicas: 100, + prenormalizedDesiredReplicas: 200, + expectedReplicas: 200, + expectedCondition: "DesiredWithinRange", + }, + { + name: "check 'outdated' flag for events for one behavior for down", + scaleDownEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(1000, 60, 0, 0, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, + expectedCondition: "DesiredWithinRange", + }, + { + name: "check that events were not marked 'outdated' for two different policies in the behavior for down", + scaleDownEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(1000, 120, 100, 60, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, + expectedCondition: "DesiredWithinRange", + }, + { + name: "check that events were marked 'outdated' for two different policies in the behavior for down", + scaleDownEvents: generateEventsUniformDistribution([]int{8, 12, 9, 11}, 120), + specMinReplicas: 1, + specMaxReplicas: 1000, + scaleDownRules: generateScalingRules(1000, 30, 100, 60, 300), + currentReplicas: 100, + prenormalizedDesiredReplicas: 5, + expectedReplicas: 5, + expectedCondition: "DesiredWithinRange", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + + if tc.testThis { + return + } + hc := HorizontalController{ + scaleUpEvents: map[string][]timestampedScaleEvent{ + tc.key: tc.scaleUpEvents, + }, + scaleDownEvents: map[string][]timestampedScaleEvent{ + tc.key: tc.scaleDownEvents, + }, + } + arg := NormalizationArg{ + Key: tc.key, + ScaleUpBehavior: autoscalingapiv2beta2.GenerateHPAScaleUpRules(tc.scaleUpRules), + ScaleDownBehavior: autoscalingapiv2beta2.GenerateHPAScaleDownRules(tc.scaleDownRules), + MinReplicas: tc.specMinReplicas, + MaxReplicas: tc.specMaxReplicas, + DesiredReplicas: tc.prenormalizedDesiredReplicas, + CurrentReplicas: tc.currentReplicas, + } + + replicas, condition, _ := hc.convertDesiredReplicasWithBehaviorRate(arg) + assert.Equal(t, tc.expectedReplicas, replicas, "expected replicas do not match with converted replicas") + assert.Equal(t, tc.expectedCondition, condition, "HPA condition does not match with expected condition") + }) + } + +} + +// TestStoreScaleEvents tests events storage and usage +func TestStoreScaleEvents(t *testing.T) { + type TestCase struct { + name string + key string + replicaChange int32 + prevScaleEvents []timestampedScaleEvent + newScaleEvents []timestampedScaleEvent + scalingRules *autoscalingv2.HPAScalingRules + expectedReplicasChange int32 + } + tests := []TestCase{ + { + name: "empty entries with default behavior", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{}, // no history -> 0 replica change + newScaleEvents: []timestampedScaleEvent{}, // no behavior -> no events are stored + expectedReplicasChange: 0, + }, + { + name: "empty entries with two-policy-behavior", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{}, // no history -> 0 replica change + newScaleEvents: []timestampedScaleEvent{{5, time.Now(), false}}, + scalingRules: generateScalingRules(10, 60, 100, 60, 0), + expectedReplicasChange: 0, + }, + { + name: "one outdated entry to be kept untouched without behavior", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {7, time.Now().Add(-time.Second * time.Duration(61)), false}, // outdated event, should be replaced + }, + newScaleEvents: []timestampedScaleEvent{ + {7, time.Now(), false}, // no behavior -> we don't touch stored events + }, + expectedReplicasChange: 0, + }, + { + name: "one outdated entry to be replaced with behavior", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {7, time.Now().Add(-time.Second * time.Duration(61)), false}, // outdated event, should be replaced + }, + newScaleEvents: []timestampedScaleEvent{ + {5, time.Now(), false}, + }, + scalingRules: generateScalingRules(10, 60, 100, 60, 0), + expectedReplicasChange: 0, + }, + { + name: "one actual entry to be not touched with behavior", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {7, time.Now().Add(-time.Second * time.Duration(58)), false}, + }, + newScaleEvents: []timestampedScaleEvent{ + {7, time.Now(), false}, + {5, time.Now(), false}, + }, + scalingRules: generateScalingRules(10, 60, 100, 60, 0), + expectedReplicasChange: 7, + }, + { + name: "two entries, one of them to be replaced", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {7, time.Now().Add(-time.Second * time.Duration(61)), false}, // outdated event, should be replaced + {6, time.Now().Add(-time.Second * time.Duration(59)), false}, + }, + newScaleEvents: []timestampedScaleEvent{ + {5, time.Now(), false}, + {6, time.Now(), false}, + }, + scalingRules: generateScalingRules(10, 60, 0, 0, 0), + expectedReplicasChange: 6, + }, + { + name: "replace one entry, use policies with different periods", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {8, time.Now().Add(-time.Second * time.Duration(29)), false}, + {6, time.Now().Add(-time.Second * time.Duration(59)), false}, + {7, time.Now().Add(-time.Second * time.Duration(61)), false}, // outdated event, should be marked as outdated + {9, time.Now().Add(-time.Second * time.Duration(61)), false}, // outdated event, should be replaced + }, + newScaleEvents: []timestampedScaleEvent{ + {8, time.Now(), false}, + {6, time.Now(), false}, + {7, time.Now(), true}, + {5, time.Now(), false}, + }, + scalingRules: generateScalingRules(10, 60, 100, 30, 0), + expectedReplicasChange: 14, + }, + { + name: "two entries, both actual", + replicaChange: 5, + prevScaleEvents: []timestampedScaleEvent{ + {7, time.Now().Add(-time.Second * time.Duration(58)), false}, + {6, time.Now().Add(-time.Second * time.Duration(59)), false}, + }, + newScaleEvents: []timestampedScaleEvent{ + {7, time.Now(), false}, + {6, time.Now(), false}, + {5, time.Now(), false}, + }, + scalingRules: generateScalingRules(10, 120, 100, 30, 0), + expectedReplicasChange: 13, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // testing scale up + var behaviorUp *autoscalingv2.HorizontalPodAutoscalerBehavior + if tc.scalingRules != nil { + behaviorUp = &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: tc.scalingRules, + } + } + hcUp := HorizontalController{ + scaleUpEvents: map[string][]timestampedScaleEvent{ + tc.key: append([]timestampedScaleEvent{}, tc.prevScaleEvents...), + }, + } + gotReplicasChangeUp := getReplicasChangePerPeriod(60, hcUp.scaleUpEvents[tc.key]) + assert.Equal(t, tc.expectedReplicasChange, gotReplicasChangeUp) + hcUp.storeScaleEvent(behaviorUp, tc.key, 10, 10+tc.replicaChange) + if !assert.Len(t, hcUp.scaleUpEvents[tc.key], len(tc.newScaleEvents), "up: scale events differ in length") { + return + } + for i, gotEvent := range hcUp.scaleUpEvents[tc.key] { + expEvent := tc.newScaleEvents[i] + assert.Equal(t, expEvent.replicaChange, gotEvent.replicaChange, "up: idx:%v replicaChange", i) + assert.Equal(t, expEvent.outdated, gotEvent.outdated, "up: idx:%v outdated", i) + } + // testing scale down + var behaviorDown *autoscalingv2.HorizontalPodAutoscalerBehavior + if tc.scalingRules != nil { + behaviorDown = &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleDown: tc.scalingRules, + } + } + hcDown := HorizontalController{ + scaleDownEvents: map[string][]timestampedScaleEvent{ + tc.key: append([]timestampedScaleEvent{}, tc.prevScaleEvents...), + }, + } + gotReplicasChangeDown := getReplicasChangePerPeriod(60, hcDown.scaleDownEvents[tc.key]) + assert.Equal(t, tc.expectedReplicasChange, gotReplicasChangeDown) + hcDown.storeScaleEvent(behaviorDown, tc.key, 10, 10-tc.replicaChange) + if !assert.Len(t, hcDown.scaleDownEvents[tc.key], len(tc.newScaleEvents), "down: scale events differ in length") { + return + } + for i, gotEvent := range hcDown.scaleDownEvents[tc.key] { + expEvent := tc.newScaleEvents[i] + assert.Equal(t, expEvent.replicaChange, gotEvent.replicaChange, "down: idx:%v replicaChange", i) + assert.Equal(t, expEvent.outdated, gotEvent.outdated, "down: idx:%v outdated", i) + } + }) + } +} + +func TestNormalizeDesiredReplicasWithBehavior(t *testing.T) { + type TestCase struct { + name string + key string + recommendations []timestampedRecommendation + currentReplicas int32 + prenormalizedDesiredReplicas int32 + expectedStabilizedReplicas int32 + expectedRecommendations []timestampedRecommendation + scaleUpStabilizationWindowSeconds int32 + scaleDownStabilizationWindowSeconds int32 + } + tests := []TestCase{ + { + name: "empty recommendations for scaling down", + key: "", + recommendations: []timestampedRecommendation{}, + currentReplicas: 100, + prenormalizedDesiredReplicas: 5, + expectedStabilizedReplicas: 5, + expectedRecommendations: []timestampedRecommendation{ + {5, time.Now()}, + }, + }, + { + name: "simple scale down stabilization", + key: "", + recommendations: []timestampedRecommendation{ + {4, time.Now().Add(-2 * time.Minute)}, + {5, time.Now().Add(-1 * time.Minute)}}, + currentReplicas: 100, + prenormalizedDesiredReplicas: 3, + expectedStabilizedReplicas: 5, + expectedRecommendations: []timestampedRecommendation{ + {4, time.Now()}, + {5, time.Now()}, + {3, time.Now()}, + }, + scaleDownStabilizationWindowSeconds: 60 * 3, + }, + { + name: "simple scale up stabilization", + key: "", + recommendations: []timestampedRecommendation{ + {4, time.Now().Add(-2 * time.Minute)}, + {5, time.Now().Add(-1 * time.Minute)}}, + currentReplicas: 1, + prenormalizedDesiredReplicas: 7, + expectedStabilizedReplicas: 4, + expectedRecommendations: []timestampedRecommendation{ + {4, time.Now()}, + {5, time.Now()}, + {7, time.Now()}, + }, + scaleUpStabilizationWindowSeconds: 60 * 5, + }, + { + name: "no scale down stabilization", + key: "", + recommendations: []timestampedRecommendation{ + {1, time.Now().Add(-2 * time.Minute)}, + {2, time.Now().Add(-1 * time.Minute)}}, + currentReplicas: 100, // to apply scaleDown delay we should have current > desired + prenormalizedDesiredReplicas: 3, + expectedStabilizedReplicas: 3, + expectedRecommendations: []timestampedRecommendation{ + {1, time.Now()}, + {2, time.Now()}, + {3, time.Now()}, + }, + scaleUpStabilizationWindowSeconds: 60 * 5, + }, + { + name: "no scale up stabilization", + key: "", + recommendations: []timestampedRecommendation{ + {4, time.Now().Add(-2 * time.Minute)}, + {5, time.Now().Add(-1 * time.Minute)}}, + currentReplicas: 1, // to apply scaleDown delay we should have current > desired + prenormalizedDesiredReplicas: 3, + expectedStabilizedReplicas: 3, + expectedRecommendations: []timestampedRecommendation{ + {4, time.Now()}, + {5, time.Now()}, + {3, time.Now()}, + }, + scaleDownStabilizationWindowSeconds: 60 * 5, + }, + { + name: "no scale down stabilization, reuse recommendation element", + key: "", + recommendations: []timestampedRecommendation{ + {10, time.Now().Add(-10 * time.Minute)}, + {9, time.Now().Add(-9 * time.Minute)}}, + currentReplicas: 100, // to apply scaleDown delay we should have current > desired + prenormalizedDesiredReplicas: 3, + expectedStabilizedReplicas: 3, + expectedRecommendations: []timestampedRecommendation{ + {10, time.Now()}, + {3, time.Now()}, + }, + }, + { + name: "no scale up stabilization, reuse recommendation element", + key: "", + recommendations: []timestampedRecommendation{ + {10, time.Now().Add(-10 * time.Minute)}, + {9, time.Now().Add(-9 * time.Minute)}}, + currentReplicas: 1, + prenormalizedDesiredReplicas: 100, + expectedStabilizedReplicas: 100, + expectedRecommendations: []timestampedRecommendation{ + {10, time.Now()}, + {100, time.Now()}, + }, + }, + { + name: "scale down stabilization, reuse one of obsolete recommendation element", + key: "", + recommendations: []timestampedRecommendation{ + {10, time.Now().Add(-10 * time.Minute)}, + {4, time.Now().Add(-1 * time.Minute)}, + {5, time.Now().Add(-2 * time.Minute)}, + {9, time.Now().Add(-9 * time.Minute)}}, + currentReplicas: 100, + prenormalizedDesiredReplicas: 3, + expectedStabilizedReplicas: 5, + expectedRecommendations: []timestampedRecommendation{ + {10, time.Now()}, + {4, time.Now()}, + {5, time.Now()}, + {3, time.Now()}, + }, + scaleDownStabilizationWindowSeconds: 3 * 60, + }, + { + // we can reuse only the first recommendation element + // as the scale up delay = 150 (set in test), scale down delay = 300 (by default) + // hence, only the first recommendation is obsolete for both scale up and scale down + name: "scale up stabilization, reuse one of obsolete recommendation element", + key: "", + recommendations: []timestampedRecommendation{ + {10, time.Now().Add(-100 * time.Minute)}, + {6, time.Now().Add(-1 * time.Minute)}, + {5, time.Now().Add(-2 * time.Minute)}, + {9, time.Now().Add(-3 * time.Minute)}}, + currentReplicas: 1, + prenormalizedDesiredReplicas: 100, + expectedStabilizedReplicas: 5, + expectedRecommendations: []timestampedRecommendation{ + {100, time.Now()}, + {6, time.Now()}, + {5, time.Now()}, + {9, time.Now()}, + }, + scaleUpStabilizationWindowSeconds: 300, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + hc := HorizontalController{ + recommendations: map[string][]timestampedRecommendation{ + tc.key: tc.recommendations, + }, + } + arg := NormalizationArg{ + Key: tc.key, + DesiredReplicas: tc.prenormalizedDesiredReplicas, + CurrentReplicas: tc.currentReplicas, + ScaleUpBehavior: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &tc.scaleUpStabilizationWindowSeconds, + }, + ScaleDownBehavior: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &tc.scaleDownStabilizationWindowSeconds, + }, + } + r, _, _ := hc.stabilizeRecommendationWithBehaviors(arg) + assert.Equal(t, tc.expectedStabilizedReplicas, r, "expected replicas do not match") + if !assert.Len(t, hc.recommendations[tc.key], len(tc.expectedRecommendations), "stored recommendations differ in length") { + return + } + for i, r := range hc.recommendations[tc.key] { + expectedRecommendation := tc.expectedRecommendations[i] + assert.Equal(t, expectedRecommendation.recommendation, r.recommendation, "stored recommendation differs at position %d", i) + } + }) + } +} + func TestScaleUpOneMetricEmpty(t *testing.T) { tc := testCase{ minReplicas: 2, @@ -3125,5 +4045,3 @@ func TestNoScaleDownOneMetricEmpty(t *testing.T) { tc.testEMClient = testEMClient tc.runTest(t) } - -// TODO: add more tests diff --git a/pkg/controller/podautoscaler/legacy_horizontal_test.go b/pkg/controller/podautoscaler/legacy_horizontal_test.go index a0621a36d33..31bcab4af18 100644 --- a/pkg/controller/podautoscaler/legacy_horizontal_test.go +++ b/pkg/controller/podautoscaler/legacy_horizontal_test.go @@ -29,7 +29,7 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2 "k8s.io/api/autoscaling/v2beta1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta/testrestmapper" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe_test.go index 1a667b2872c..bde4fc051c3 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe_test.go @@ -1773,6 +1773,7 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) { minReplicasVal := int32(2) targetUtilizationVal := int32(80) currentUtilizationVal := int32(50) + maxSelectPolicy := autoscalingv2beta2.MaxPolicySelect metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") if err != nil { t.Errorf("unable to parse label selector: %v", err) @@ -2417,6 +2418,75 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) { }, }, }, + { + "scale up behavior specified", + autoscalingv2beta2.HorizontalPodAutoscaler{ + Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{ + Name: "behavior-target", + Kind: "Deployment", + }, + MinReplicas: &minReplicasVal, + MaxReplicas: 10, + Metrics: []autoscalingv2beta2.MetricSpec{ + { + Type: autoscalingv2beta2.ResourceMetricSourceType, + Resource: &autoscalingv2beta2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2beta2.MetricTarget{ + Type: autoscalingv2beta2.UtilizationMetricType, + AverageUtilization: &targetUtilizationVal, + }, + }, + }, + }, + Behavior: &autoscalingv2beta2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2beta2.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(30), + SelectPolicy: &maxSelectPolicy, + Policies: []autoscalingv2beta2.HPAScalingPolicy{ + {Type: autoscalingv2beta2.PodsScalingPolicy, Value: 10, PeriodSeconds: 10}, + {Type: autoscalingv2beta2.PercentScalingPolicy, Value: 10, PeriodSeconds: 10}, + }, + }, + }, + }, + }, + }, + { + "scale down behavior specified", + autoscalingv2beta2.HorizontalPodAutoscaler{ + Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{ + Name: "behavior-target", + Kind: "Deployment", + }, + MinReplicas: &minReplicasVal, + MaxReplicas: 10, + Metrics: []autoscalingv2beta2.MetricSpec{ + { + Type: autoscalingv2beta2.ResourceMetricSourceType, + Resource: &autoscalingv2beta2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2beta2.MetricTarget{ + Type: autoscalingv2beta2.UtilizationMetricType, + AverageUtilization: &targetUtilizationVal, + }, + }, + }, + }, + Behavior: &autoscalingv2beta2.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscalingv2beta2.HPAScalingRules{ + StabilizationWindowSeconds: utilpointer.Int32Ptr(30), + Policies: []autoscalingv2beta2.HPAScalingPolicy{ + {Type: autoscalingv2beta2.PodsScalingPolicy, Value: 10, PeriodSeconds: 10}, + {Type: autoscalingv2beta2.PercentScalingPolicy, Value: 10, PeriodSeconds: 10}, + }, + }, + }, + }, + }, + }, } for _, test := range testsV2beta2 {