2016-02-24 22:33:28 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-02-24 22:33:28 +00:00
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 validation
import (
2016-11-30 19:36:52 +00:00
"strings"
2016-05-05 10:27:24 +00:00
2018-08-11 14:17:29 +00:00
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
2017-01-26 19:38:21 +00:00
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
2016-11-30 19:36:52 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/validation/field"
2016-02-24 22:33:28 +00:00
"k8s.io/kubernetes/pkg/apis/autoscaling"
2017-11-08 22:34:54 +00:00
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
2016-02-24 22:33:28 +00:00
)
func ValidateScale ( scale * autoscaling . Scale ) field . ErrorList {
allErrs := field . ErrorList { }
2018-08-11 14:17:29 +00:00
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & scale . ObjectMeta , true , apimachineryvalidation . NameIsDNSSubdomain , field . NewPath ( "metadata" ) ) ... )
2016-02-24 22:33:28 +00:00
if scale . Spec . Replicas < 0 {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" , "replicas" ) , scale . Spec . Replicas , "must be greater than or equal to 0" ) )
}
return allErrs
}
2016-05-05 10:27:24 +00:00
// ValidateHorizontalPodAutoscaler can be used to check whether the given autoscaler name is valid.
// Prefix indicates this name will be used as part of generation, in which case trailing dashes are allowed.
2015-12-16 06:03:20 +00:00
var ValidateHorizontalPodAutoscalerName = apivalidation . ValidateReplicationControllerName
2016-05-05 10:27:24 +00:00
func validateHorizontalPodAutoscalerSpec ( autoscaler autoscaling . HorizontalPodAutoscalerSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if autoscaler . MinReplicas != nil && * autoscaler . MinReplicas < 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "minReplicas" ) , * autoscaler . MinReplicas , "must be greater than 0" ) )
}
if autoscaler . MaxReplicas < 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "maxReplicas" ) , autoscaler . MaxReplicas , "must be greater than 0" ) )
}
if autoscaler . MinReplicas != nil && autoscaler . MaxReplicas < * autoscaler . MinReplicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "maxReplicas" ) , autoscaler . MaxReplicas , "must be greater than or equal to `minReplicas`" ) )
}
if refErrs := ValidateCrossVersionObjectReference ( autoscaler . ScaleTargetRef , fldPath . Child ( "scaleTargetRef" ) ) ; len ( refErrs ) > 0 {
allErrs = append ( allErrs , refErrs ... )
}
2016-11-30 19:36:52 +00:00
if refErrs := validateMetrics ( autoscaler . Metrics , fldPath . Child ( "metrics" ) ) ; len ( refErrs ) > 0 {
allErrs = append ( allErrs , refErrs ... )
}
2016-05-05 10:27:24 +00:00
return allErrs
}
func ValidateCrossVersionObjectReference ( ref autoscaling . CrossVersionObjectReference , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( ref . Kind ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "kind" ) , "" ) )
2015-12-16 06:03:20 +00:00
} else {
2016-08-24 17:37:28 +00:00
for _ , msg := range pathvalidation . IsValidPathSegmentName ( ref . Kind ) {
2015-12-16 06:03:20 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "kind" ) , ref . Kind , msg ) )
}
2016-05-05 10:27:24 +00:00
}
if len ( ref . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "" ) )
2015-12-16 06:03:20 +00:00
} else {
2016-08-24 17:37:28 +00:00
for _ , msg := range pathvalidation . IsValidPathSegmentName ( ref . Name ) {
2015-12-16 06:03:20 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , ref . Name , msg ) )
}
2016-05-05 10:27:24 +00:00
}
return allErrs
}
func ValidateHorizontalPodAutoscaler ( autoscaler * autoscaling . HorizontalPodAutoscaler ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMeta ( & autoscaler . ObjectMeta , true , ValidateHorizontalPodAutoscalerName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , validateHorizontalPodAutoscalerSpec ( autoscaler . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
func ValidateHorizontalPodAutoscalerUpdate ( newAutoscaler , oldAutoscaler * autoscaling . HorizontalPodAutoscaler ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & newAutoscaler . ObjectMeta , & oldAutoscaler . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , validateHorizontalPodAutoscalerSpec ( newAutoscaler . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
func ValidateHorizontalPodAutoscalerStatusUpdate ( newAutoscaler , oldAutoscaler * autoscaling . HorizontalPodAutoscaler ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & newAutoscaler . ObjectMeta , & oldAutoscaler . ObjectMeta , field . NewPath ( "metadata" ) )
status := newAutoscaler . Status
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . CurrentReplicas ) , field . NewPath ( "status" , "currentReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . DesiredReplicas ) , field . NewPath ( "status" , "desiredReplicasa" ) ) ... )
return allErrs
}
2016-11-30 19:36:52 +00:00
func validateMetrics ( metrics [ ] autoscaling . MetricSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
for i , metricSpec := range metrics {
idxPath := fldPath . Index ( i )
if targetErrs := validateMetricSpec ( metricSpec , idxPath ) ; len ( targetErrs ) > 0 {
allErrs = append ( allErrs , targetErrs ... )
}
}
return allErrs
}
2018-02-21 10:47:52 +00:00
var validMetricSourceTypes = sets . NewString ( string ( autoscaling . ObjectMetricSourceType ) , string ( autoscaling . PodsMetricSourceType ) , string ( autoscaling . ResourceMetricSourceType ) , string ( autoscaling . ExternalMetricSourceType ) )
2016-11-30 19:36:52 +00:00
var validMetricSourceTypesList = validMetricSourceTypes . List ( )
func validateMetricSpec ( spec autoscaling . MetricSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( string ( spec . Type ) ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "type" ) , "must specify a metric source type" ) )
}
if ! validMetricSourceTypes . Has ( string ( spec . Type ) ) {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "type" ) , spec . Type , validMetricSourceTypesList ) )
}
typesPresent := sets . NewString ( )
if spec . Object != nil {
typesPresent . Insert ( "object" )
if typesPresent . Len ( ) == 1 {
allErrs = append ( allErrs , validateObjectSource ( spec . Object , fldPath . Child ( "object" ) ) ... )
}
}
2018-02-21 10:47:52 +00:00
if spec . External != nil {
typesPresent . Insert ( "external" )
if typesPresent . Len ( ) == 1 {
allErrs = append ( allErrs , validateExternalSource ( spec . External , fldPath . Child ( "external" ) ) ... )
}
}
2016-11-30 19:36:52 +00:00
if spec . Pods != nil {
typesPresent . Insert ( "pods" )
if typesPresent . Len ( ) == 1 {
allErrs = append ( allErrs , validatePodsSource ( spec . Pods , fldPath . Child ( "pods" ) ) ... )
}
}
if spec . Resource != nil {
typesPresent . Insert ( "resource" )
if typesPresent . Len ( ) == 1 {
allErrs = append ( allErrs , validateResourceSource ( spec . Resource , fldPath . Child ( "resource" ) ) ... )
}
}
expectedField := strings . ToLower ( string ( spec . Type ) )
if ! typesPresent . Has ( expectedField ) {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( expectedField ) , "must populate information for the given metric source" ) )
}
if typesPresent . Len ( ) != 1 {
typesPresent . Delete ( expectedField )
for typ := range typesPresent {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( typ ) , "must populate the given metric source only" ) )
}
}
return allErrs
}
func validateObjectSource ( src * autoscaling . ObjectMetricSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2018-06-28 18:25:58 +00:00
allErrs = append ( allErrs , ValidateCrossVersionObjectReference ( src . DescribedObject , fldPath . Child ( "describedObject" ) ) ... )
allErrs = append ( allErrs , validateMetricIdentifier ( src . Metric , fldPath . Child ( "metric" ) ) ... )
if & src . Target == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) , "must specify a metric target" ) )
} else {
allErrs = append ( allErrs , validateMetricTarget ( src . Target , fldPath . Child ( "target" ) ) ... )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
if src . Target . Value == nil && src . Target . AverageValue == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) . Child ( "averageValue" ) , "must set either a target value or averageValue" ) )
2016-11-30 19:36:52 +00:00
}
return allErrs
}
2018-02-21 10:47:52 +00:00
func validateExternalSource ( src * autoscaling . ExternalMetricSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2018-06-28 18:25:58 +00:00
allErrs = append ( allErrs , validateMetricIdentifier ( src . Metric , fldPath . Child ( "metric" ) ) ... )
if & src . Target == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) , "must specify a metric target" ) )
2018-02-26 16:44:55 +00:00
} else {
2018-06-28 18:25:58 +00:00
allErrs = append ( allErrs , validateMetricTarget ( src . Target , fldPath . Child ( "target" ) ) ... )
2018-02-21 10:47:52 +00:00
}
2018-06-28 18:25:58 +00:00
if src . Target . Value == nil && src . Target . AverageValue == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) . Child ( "averageValue" ) , "must set either a target value for metric or a per-pod target" ) )
2018-02-21 10:47:52 +00:00
}
2018-06-28 18:25:58 +00:00
if src . Target . Value != nil && src . Target . AverageValue != nil {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "target" ) . Child ( "value" ) , "may not set both a target value for metric and a per-pod target" ) )
2018-02-21 10:47:52 +00:00
}
return allErrs
}
2016-11-30 19:36:52 +00:00
func validatePodsSource ( src * autoscaling . PodsMetricSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2018-06-28 18:25:58 +00:00
allErrs = append ( allErrs , validateMetricIdentifier ( src . Metric , fldPath . Child ( "metric" ) ) ... )
if & src . Target == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) , "must specify a metric target" ) )
} else {
allErrs = append ( allErrs , validateMetricTarget ( src . Target , fldPath . Child ( "target" ) ) ... )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
if src . Target . AverageValue == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) . Child ( "averageValue" ) , "must specify a positive target averageValue" ) )
2016-11-30 19:36:52 +00:00
}
return allErrs
}
func validateResourceSource ( src * autoscaling . ResourceMetricSource , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( src . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "must specify a resource name" ) )
}
2018-06-28 18:25:58 +00:00
if & src . Target == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) , "must specify a metric target" ) )
} else {
allErrs = append ( allErrs , validateMetricTarget ( src . Target , fldPath . Child ( "target" ) ) ... )
}
2016-11-30 19:36:52 +00:00
2018-06-28 18:25:58 +00:00
if src . Target . AverageUtilization == nil && src . Target . AverageValue == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "target" ) . Child ( "averageUtilization" ) , "must set either a target raw value or a target utilization" ) )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
if src . Target . AverageUtilization != nil && src . Target . AverageValue != nil {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "target" ) . Child ( "averageValue" ) , "may not set both a target raw value and a target utilization" ) )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
return allErrs
}
func validateMetricTarget ( mt autoscaling . MetricTarget , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( mt . Type ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "type" ) , "must specify a metric target type" ) )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
if mt . Type != autoscaling . UtilizationMetricType &&
mt . Type != autoscaling . ValueMetricType &&
mt . Type != autoscaling . AverageValueMetricType {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "type" ) , mt . Type , "must be either Utilization, Value, or AverageValue" ) )
2016-11-30 19:36:52 +00:00
}
2018-06-28 18:25:58 +00:00
if mt . Value != nil && mt . Value . Sign ( ) != 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "value" ) , mt . Value , "must be positive" ) )
}
if mt . AverageValue != nil && mt . AverageValue . Sign ( ) != 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "averageValue" ) , mt . AverageValue , "must be positive" ) )
}
if mt . AverageUtilization != nil && * mt . AverageUtilization < 1 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "averageUtilization" ) , mt . AverageUtilization , "must be greater than 0" ) )
}
return allErrs
}
func validateMetricIdentifier ( id autoscaling . MetricIdentifier , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if len ( id . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "name" ) , "must specify a metric name" ) )
} else {
for _ , msg := range pathvalidation . IsValidPathSegmentName ( id . Name ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "name" ) , id . Name , msg ) )
}
}
2016-11-30 19:36:52 +00:00
return allErrs
}