From 141eaf79eed58481a51569caf0df7deba016f111 Mon Sep 17 00:00:00 2001 From: Ivan Glushkov Date: Mon, 14 Oct 2019 19:13:17 +0400 Subject: [PATCH] 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