Adds validation rules and proper defaults

This commit is contained in:
Ivan Glushkov 2019-12-10 19:48:46 +04:00
parent 141eaf79ee
commit 5c70cda6e5
No known key found for this signature in database
GPG Key ID: 10C5CFB0C77CA594
2 changed files with 158 additions and 1 deletions

View File

@ -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
}

View File

@ -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()