mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #74525 from gliush/configurable-hpa-limits
Configurable HorizontalPodAutoscaler
This commit is contained in:
commit
928817a26a
@ -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
|
||||
|
65
api/openapi-spec/swagger.json
generated
65
api/openapi-spec/swagger.json
generated
@ -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",
|
||||
|
@ -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"}: {},
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
231
pkg/apis/autoscaling/v2beta2/defaults_test.go
Normal file
231
pkg/apis/autoscaling/v2beta2/defaults_test.go
Normal file
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
102
pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go
generated
102
pkg/apis/autoscaling/v2beta2/zz_generated.conversion.go
generated
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
|
78
pkg/apis/autoscaling/zz_generated.deepcopy.go
generated
78
pkg/apis/autoscaling/zz_generated.deepcopy.go
generated
@ -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
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
|
Binary file not shown.
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user