2015-08-06 14:14:28 +00:00
/ *
Copyright 2015 The Kubernetes Authors All rights reserved .
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 (
2015-09-23 03:20:13 +00:00
"fmt"
"net"
"regexp"
2015-08-25 19:07:03 +00:00
"strconv"
2015-09-23 03:20:13 +00:00
"strings"
2015-08-25 19:07:03 +00:00
2015-08-06 14:14:28 +00:00
"k8s.io/kubernetes/pkg/api"
apivalidation "k8s.io/kubernetes/pkg/api/validation"
2015-10-09 22:04:41 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
2015-08-27 17:17:41 +00:00
"k8s.io/kubernetes/pkg/labels"
2015-08-15 05:10:15 +00:00
"k8s.io/kubernetes/pkg/util"
2015-08-06 14:14:28 +00:00
errs "k8s.io/kubernetes/pkg/util/fielderrors"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2015-09-10 22:48:28 +00:00
"k8s.io/kubernetes/pkg/util/validation"
2015-09-23 03:20:13 +00:00
utilvalidation "k8s.io/kubernetes/pkg/util/validation"
2015-08-06 14:14:28 +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.
func ValidateHorizontalPodAutoscalerName ( name string , prefix bool ) ( bool , string ) {
// TODO: finally move it to pkg/api/validation and use nameIsDNSSubdomain function
return apivalidation . ValidateReplicationControllerName ( name , prefix )
}
2015-10-09 22:49:10 +00:00
func validateHorizontalPodAutoscalerSpec ( autoscaler extensions . HorizontalPodAutoscalerSpec ) errs . ValidationErrorList {
2015-08-06 14:14:28 +00:00
allErrs := errs . ValidationErrorList { }
2015-10-13 15:24:23 +00:00
if autoscaler . MinReplicas != nil && * autoscaler . MinReplicas < 1 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "minReplicas" , autoscaler . MinReplicas , ` must be bigger or equal to 1 ` ) )
2015-08-06 14:14:28 +00:00
}
2015-10-13 15:24:23 +00:00
if autoscaler . MaxReplicas < 1 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "maxReplicas" , autoscaler . MaxReplicas , ` must be bigger or equal to 1 ` ) )
2015-08-06 14:14:28 +00:00
}
2015-10-13 15:24:23 +00:00
if autoscaler . MinReplicas != nil && autoscaler . MaxReplicas < * autoscaler . MinReplicas {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "maxReplicas" , autoscaler . MaxReplicas , ` must be bigger or equal to minReplicas ` ) )
2015-08-06 14:14:28 +00:00
}
2015-10-13 15:24:23 +00:00
if autoscaler . CPUUtilization != nil && autoscaler . CPUUtilization . TargetPercentage < 1 {
2015-10-16 10:24:34 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "cpuUtilization.targetPercentage" , autoscaler . CPUUtilization . TargetPercentage , ` must be bigger or equal to 1 ` ) )
2015-08-06 14:14:28 +00:00
}
2015-11-03 06:55:32 +00:00
if refErrs := ValidateSubresourceReference ( autoscaler . ScaleRef ) ; len ( refErrs ) > 0 {
allErrs = append ( allErrs , refErrs . Prefix ( "scaleRef" ) ... )
} else if autoscaler . ScaleRef . Subresource != "scale" {
allErrs = append ( allErrs , errs . NewFieldValueNotSupported ( "scaleRef.subresource" , autoscaler . ScaleRef . Subresource , [ ] string { "scale" } ) )
}
return allErrs
}
func ValidateSubresourceReference ( ref extensions . SubresourceReference ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if len ( ref . Kind ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "kind" ) )
} else if ok , msg := apivalidation . IsValidPathSegmentName ( ref . Kind ) ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "kind" , ref . Kind , msg ) )
}
if len ( ref . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "name" ) )
} else if ok , msg := apivalidation . IsValidPathSegmentName ( ref . Name ) ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , ref . Name , msg ) )
}
if len ( ref . Subresource ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "subresource" ) )
} else if ok , msg := apivalidation . IsValidPathSegmentName ( ref . Subresource ) ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "subresource" , ref . Subresource , msg ) )
}
2015-08-06 14:14:28 +00:00
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateHorizontalPodAutoscaler ( autoscaler * extensions . HorizontalPodAutoscaler ) errs . ValidationErrorList {
2015-08-06 14:14:28 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & autoscaler . ObjectMeta , true , ValidateHorizontalPodAutoscalerName ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , validateHorizontalPodAutoscalerSpec ( autoscaler . Spec ) ... )
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateHorizontalPodAutoscalerUpdate ( newAutoscler , oldAutoscaler * extensions . HorizontalPodAutoscaler ) errs . ValidationErrorList {
2015-08-06 14:14:28 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & newAutoscler . ObjectMeta , & oldAutoscaler . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , validateHorizontalPodAutoscalerSpec ( newAutoscler . Spec ) ... )
return allErrs
}
2015-08-15 05:10:15 +00:00
2015-09-25 17:57:05 +00:00
func ValidateHorizontalPodAutoscalerStatusUpdate ( controller , oldController * extensions . HorizontalPodAutoscaler ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta ) . Prefix ( "metadata" ) ... )
status := controller . Status
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . CurrentReplicas ) , "currentReplicas" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . DesiredReplicas ) , "desiredReplicas" ) ... )
return allErrs
}
2015-11-04 07:47:11 +00:00
func ValidateThirdPartyResourceUpdate ( update , old * extensions . ThirdPartyResource ) errs . ValidationErrorList {
2015-08-15 05:10:15 +00:00
return ValidateThirdPartyResource ( update )
}
2015-10-09 22:49:10 +00:00
func ValidateThirdPartyResource ( obj * extensions . ThirdPartyResource ) errs . ValidationErrorList {
2015-08-15 05:10:15 +00:00
allErrs := errs . ValidationErrorList { }
if len ( obj . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , obj . Name , "name must be non-empty" ) )
}
2015-09-09 17:45:01 +00:00
versions := sets . String { }
2015-08-15 05:10:15 +00:00
for ix := range obj . Versions {
version := & obj . Versions [ ix ]
if len ( version . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , version , "name can not be empty" ) )
}
if versions . Has ( version . Name ) {
allErrs = append ( allErrs , errs . NewFieldDuplicate ( "version" , version ) )
}
versions . Insert ( version . Name )
}
return allErrs
}
2015-08-27 17:17:41 +00:00
2015-09-08 23:58:25 +00:00
// ValidateDaemonSet tests if required fields in the DaemonSet are set.
2015-10-09 22:49:10 +00:00
func ValidateDaemonSet ( controller * extensions . DaemonSet ) errs . ValidationErrorList {
2015-08-27 17:17:41 +00:00
allErrs := errs . ValidationErrorList { }
2015-10-14 17:41:08 +00:00
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & controller . ObjectMeta , true , ValidateDaemonSetName ) . Prefix ( "metadata" ) ... )
2015-09-08 23:58:25 +00:00
allErrs = append ( allErrs , ValidateDaemonSetSpec ( & controller . Spec ) . Prefix ( "spec" ) ... )
2015-08-27 17:17:41 +00:00
return allErrs
}
2015-09-08 23:58:25 +00:00
// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
2015-11-04 07:47:11 +00:00
func ValidateDaemonSetUpdate ( controller , oldController * extensions . DaemonSet ) errs . ValidationErrorList {
2015-08-27 17:17:41 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta ) . Prefix ( "metadata" ) ... )
2015-09-08 23:58:25 +00:00
allErrs = append ( allErrs , ValidateDaemonSetSpec ( & controller . Spec ) . Prefix ( "spec" ) ... )
2015-11-04 07:47:11 +00:00
allErrs = append ( allErrs , ValidateDaemonSetTemplateUpdate ( controller . Spec . Template , oldController . Spec . Template ) . Prefix ( "spec.template" ) ... )
2015-08-27 17:17:41 +00:00
return allErrs
}
2015-09-25 02:51:09 +00:00
// validateDaemonSetStatus validates a DaemonSetStatus
2015-10-09 22:49:10 +00:00
func validateDaemonSetStatus ( status * extensions . DaemonSetStatus ) errs . ValidationErrorList {
2015-09-25 02:51:09 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . CurrentNumberScheduled ) , "currentNumberScheduled" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . NumberMisscheduled ) , "numberMisscheduled" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . DesiredNumberScheduled ) , "desiredNumberScheduled" ) ... )
return allErrs
}
// ValidateDaemonSetStatus validates tests if required fields in the DaemonSet Status section
2015-10-09 22:49:10 +00:00
func ValidateDaemonSetStatusUpdate ( controller , oldController * extensions . DaemonSet ) errs . ValidationErrorList {
2015-09-25 02:51:09 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & controller . ObjectMeta , & oldController . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , validateDaemonSetStatus ( & controller . Status ) ... )
return allErrs
}
2015-09-08 23:58:25 +00:00
// ValidateDaemonSetTemplateUpdate tests that certain fields in the daemon set's pod template are not updated.
2015-11-04 07:47:11 +00:00
func ValidateDaemonSetTemplateUpdate ( podTemplate , oldPodTemplate * api . PodTemplateSpec ) errs . ValidationErrorList {
2015-08-27 17:17:41 +00:00
allErrs := errs . ValidationErrorList { }
podSpec := podTemplate . Spec
// podTemplate.Spec is not a pointer, so we can modify NodeSelector and NodeName directly.
podSpec . NodeSelector = oldPodTemplate . Spec . NodeSelector
podSpec . NodeName = oldPodTemplate . Spec . NodeName
// In particular, we do not allow updates to container images at this point.
if ! api . Semantic . DeepEqual ( oldPodTemplate . Spec , podSpec ) {
// TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
allErrs = append ( allErrs , errs . NewFieldInvalid ( "spec" , "content of spec is not printed out, please refer to the \"details\"" , "may not update fields other than spec.nodeSelector" ) )
}
return allErrs
}
2015-09-08 23:58:25 +00:00
// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
2015-10-09 22:49:10 +00:00
func ValidateDaemonSetSpec ( spec * extensions . DaemonSetSpec ) errs . ValidationErrorList {
2015-08-27 17:17:41 +00:00
allErrs := errs . ValidationErrorList { }
2015-10-26 06:11:09 +00:00
allErrs = append ( allErrs , ValidatePodSelector ( spec . Selector ) ... )
2015-08-27 17:17:41 +00:00
if spec . Template == nil {
allErrs = append ( allErrs , errs . NewFieldRequired ( "template" ) )
2015-10-26 06:11:09 +00:00
return allErrs
}
selector , err := extensions . PodSelectorAsSelector ( spec . Selector )
if err == nil && ! selector . Matches ( labels . Set ( spec . Template . Labels ) ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "template.metadata.labels" , spec . Template . Labels , "selector does not match template" ) )
2015-08-27 17:17:41 +00:00
}
2015-10-26 06:11:09 +00:00
allErrs = append ( allErrs , apivalidation . ValidatePodTemplateSpec ( spec . Template ) . Prefix ( "template" ) ... )
// Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
allErrs = append ( allErrs , apivalidation . ValidateReadOnlyPersistentDisks ( spec . Template . Spec . Volumes ) . Prefix ( "template.spec.volumes" ) ... )
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
if spec . Template . Spec . RestartPolicy != api . RestartPolicyAlways {
allErrs = append ( allErrs , errs . NewFieldValueNotSupported ( "template.spec.restartPolicy" , spec . Template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyAlways ) } ) )
}
2015-08-27 17:17:41 +00:00
return allErrs
}
2015-09-08 23:58:25 +00:00
// ValidateDaemonSetName can be used to check whether the given daemon set name is valid.
2015-08-27 17:17:41 +00:00
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
2015-09-08 23:58:25 +00:00
func ValidateDaemonSetName ( name string , prefix bool ) ( bool , string ) {
2015-08-27 17:17:41 +00:00
return apivalidation . NameIsDNSSubdomain ( name , prefix )
}
2015-08-25 19:07:03 +00:00
// Validates that the given name can be used as a deployment name.
func ValidateDeploymentName ( name string , prefix bool ) ( bool , string ) {
return apivalidation . NameIsDNSSubdomain ( name , prefix )
}
func ValidatePositiveIntOrPercent ( intOrPercent util . IntOrString , fieldName string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if intOrPercent . Kind == util . IntstrString {
2015-09-10 22:48:28 +00:00
if ! validation . IsValidPercent ( intOrPercent . StrVal ) {
2015-08-25 19:07:03 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( fieldName , intOrPercent , "value should be int(5) or percentage(5%)" ) )
}
} else if intOrPercent . Kind == util . IntstrInt {
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( intOrPercent . IntVal ) , fieldName ) ... )
}
return allErrs
}
func getPercentValue ( intOrStringValue util . IntOrString ) ( int , bool ) {
2015-09-10 22:48:28 +00:00
if intOrStringValue . Kind != util . IntstrString || ! validation . IsValidPercent ( intOrStringValue . StrVal ) {
2015-08-25 19:07:03 +00:00
return 0 , false
}
value , _ := strconv . Atoi ( intOrStringValue . StrVal [ : len ( intOrStringValue . StrVal ) - 1 ] )
return value , true
}
func getIntOrPercentValue ( intOrStringValue util . IntOrString ) int {
value , isPercent := getPercentValue ( intOrStringValue )
if isPercent {
return value
}
return intOrStringValue . IntVal
}
func IsNotMoreThan100Percent ( intOrStringValue util . IntOrString , fieldName string ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
value , isPercent := getPercentValue ( intOrStringValue )
if ! isPercent || value <= 100 {
return nil
}
allErrs = append ( allErrs , errs . NewFieldInvalid ( fieldName , intOrStringValue , "should not be more than 100%" ) )
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateRollingUpdateDeployment ( rollingUpdate * extensions . RollingUpdateDeployment , fieldName string ) errs . ValidationErrorList {
2015-08-25 19:07:03 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidatePositiveIntOrPercent ( rollingUpdate . MaxUnavailable , fieldName + "maxUnavailable" ) ... )
allErrs = append ( allErrs , ValidatePositiveIntOrPercent ( rollingUpdate . MaxSurge , fieldName + ".maxSurge" ) ... )
if getIntOrPercentValue ( rollingUpdate . MaxUnavailable ) == 0 && getIntOrPercentValue ( rollingUpdate . MaxSurge ) == 0 {
// Both MaxSurge and MaxUnavailable cannot be zero.
allErrs = append ( allErrs , errs . NewFieldInvalid ( fieldName + ".maxUnavailable" , rollingUpdate . MaxUnavailable , "cannot be 0 when maxSurge is 0 as well" ) )
}
// Validate that MaxUnavailable is not more than 100%.
allErrs = append ( allErrs , IsNotMoreThan100Percent ( rollingUpdate . MaxUnavailable , fieldName + ".maxUnavailable" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( rollingUpdate . MinReadySeconds ) , fieldName + ".minReadySeconds" ) ... )
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateDeploymentStrategy ( strategy * extensions . DeploymentStrategy , fieldName string ) errs . ValidationErrorList {
2015-08-25 19:07:03 +00:00
allErrs := errs . ValidationErrorList { }
if strategy . RollingUpdate == nil {
return allErrs
}
switch strategy . Type {
2015-10-09 22:49:10 +00:00
case extensions . RecreateDeploymentStrategyType :
allErrs = append ( allErrs , errs . NewFieldForbidden ( "rollingUpdate" , "rollingUpdate should be nil when strategy type is " + extensions . RecreateDeploymentStrategyType ) )
case extensions . RollingUpdateDeploymentStrategyType :
2015-08-25 19:07:03 +00:00
allErrs = append ( allErrs , ValidateRollingUpdateDeployment ( strategy . RollingUpdate , "rollingUpdate" ) ... )
}
return allErrs
}
// Validates given deployment spec.
2015-10-09 22:49:10 +00:00
func ValidateDeploymentSpec ( spec * extensions . DeploymentSpec ) errs . ValidationErrorList {
2015-08-25 19:07:03 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonEmptySelector ( spec . Selector , "selector" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( spec . Replicas ) , "replicas" ) ... )
2015-10-26 23:20:43 +00:00
allErrs = append ( allErrs , apivalidation . ValidatePodTemplateSpecForRC ( & spec . Template , spec . Selector , spec . Replicas , "template" ) ... )
2015-08-25 19:07:03 +00:00
allErrs = append ( allErrs , ValidateDeploymentStrategy ( & spec . Strategy , "strategy" ) ... )
2015-09-01 09:48:53 +00:00
allErrs = append ( allErrs , apivalidation . ValidateLabelName ( spec . UniqueLabelKey , "uniqueLabel" ) ... )
2015-08-25 19:07:03 +00:00
return allErrs
}
2015-11-04 07:47:11 +00:00
func ValidateDeploymentUpdate ( update , old * extensions . Deployment ) errs . ValidationErrorList {
2015-08-25 19:07:03 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & update . ObjectMeta , & old . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidateDeploymentSpec ( & update . Spec ) . Prefix ( "spec" ) ... )
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateDeployment ( obj * extensions . Deployment ) errs . ValidationErrorList {
2015-08-25 19:07:03 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & obj . ObjectMeta , true , ValidateDeploymentName ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidateDeploymentSpec ( & obj . Spec ) . Prefix ( "spec" ) ... )
return allErrs
}
2015-08-20 05:08:26 +00:00
2015-11-04 07:47:11 +00:00
func ValidateThirdPartyResourceDataUpdate ( update , old * extensions . ThirdPartyResourceData ) errs . ValidationErrorList {
2015-08-20 05:08:26 +00:00
return ValidateThirdPartyResourceData ( update )
}
2015-10-09 22:49:10 +00:00
func ValidateThirdPartyResourceData ( obj * extensions . ThirdPartyResourceData ) errs . ValidationErrorList {
2015-08-20 05:08:26 +00:00
allErrs := errs . ValidationErrorList { }
if len ( obj . Name ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , obj . Name , "name must be non-empty" ) )
}
return allErrs
}
2015-08-18 14:39:49 +00:00
2015-10-09 22:49:10 +00:00
func ValidateJob ( job * extensions . Job ) errs . ValidationErrorList {
2015-08-18 14:39:49 +00:00
allErrs := errs . ValidationErrorList { }
// Jobs and rcs have the same name validation
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & job . ObjectMeta , true , apivalidation . ValidateReplicationControllerName ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidateJobSpec ( & job . Spec ) . Prefix ( "spec" ) ... )
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateJobSpec ( spec * extensions . JobSpec ) errs . ValidationErrorList {
2015-08-18 14:39:49 +00:00
allErrs := errs . ValidationErrorList { }
2015-10-12 03:26:17 +00:00
if spec . Parallelism != nil {
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( * spec . Parallelism ) , "parallelism" ) ... )
2015-08-18 14:39:49 +00:00
}
2015-10-12 03:26:17 +00:00
if spec . Completions != nil {
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( * spec . Completions ) , "completions" ) ... )
2015-08-18 14:39:49 +00:00
}
2015-10-14 18:03:59 +00:00
if spec . Selector == nil {
2015-08-18 14:39:49 +00:00
allErrs = append ( allErrs , errs . NewFieldRequired ( "selector" ) )
2015-10-14 18:03:59 +00:00
} else {
allErrs = append ( allErrs , ValidatePodSelector ( spec . Selector ) . Prefix ( "selector" ) ... )
2015-08-18 14:39:49 +00:00
}
2015-10-14 18:03:59 +00:00
if selector , err := extensions . PodSelectorAsSelector ( spec . Selector ) ; err == nil {
labels := labels . Set ( spec . Template . Labels )
if ! selector . Matches ( labels ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "template.metadata.labels" , spec . Template . Labels , "selector does not match template" ) )
}
2015-09-25 19:07:06 +00:00
}
2015-10-14 18:03:59 +00:00
2015-09-25 19:07:06 +00:00
allErrs = append ( allErrs , apivalidation . ValidatePodTemplateSpec ( & spec . Template ) . Prefix ( "template" ) ... )
if spec . Template . Spec . RestartPolicy != api . RestartPolicyOnFailure &&
spec . Template . Spec . RestartPolicy != api . RestartPolicyNever {
allErrs = append ( allErrs , errs . NewFieldValueNotSupported ( "template.spec.restartPolicy" ,
spec . Template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyOnFailure ) , string ( api . RestartPolicyNever ) } ) )
2015-08-18 14:39:49 +00:00
}
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateJobStatus ( status * extensions . JobStatus ) errs . ValidationErrorList {
2015-09-22 08:05:54 +00:00
allErrs := errs . ValidationErrorList { }
2015-09-25 02:51:09 +00:00
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . Active ) , "active" ) ... )
2015-10-08 17:33:39 +00:00
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . Succeeded ) , "succeeded" ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePositiveField ( int64 ( status . Failed ) , "failed" ) ... )
2015-09-22 08:05:54 +00:00
return allErrs
}
2015-11-04 07:47:11 +00:00
func ValidateJobUpdate ( job , oldJob * extensions . Job ) errs . ValidationErrorList {
2015-08-18 14:39:49 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & oldJob . ObjectMeta , & job . ObjectMeta ) . Prefix ( "metadata" ) ... )
2015-11-04 07:47:11 +00:00
allErrs = append ( allErrs , ValidateJobSpecUpdate ( job . Spec , oldJob . Spec ) . Prefix ( "spec" ) ... )
2015-09-17 22:58:04 +00:00
return allErrs
}
2015-11-04 07:47:11 +00:00
func ValidateJobUpdateStatus ( job , oldJob * extensions . Job ) errs . ValidationErrorList {
2015-09-22 08:05:54 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & oldJob . ObjectMeta , & job . ObjectMeta ) . Prefix ( "metadata" ) ... )
2015-11-04 07:47:11 +00:00
allErrs = append ( allErrs , ValidateJobStatusUpdate ( job . Status , oldJob . Status ) . Prefix ( "status" ) ... )
2015-09-22 08:05:54 +00:00
return allErrs
}
2015-11-04 07:47:11 +00:00
func ValidateJobSpecUpdate ( spec , oldSpec extensions . JobSpec ) errs . ValidationErrorList {
2015-09-17 22:58:04 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateJobSpec ( & spec ) ... )
2015-11-04 07:47:11 +00:00
allErrs = append ( allErrs , apivalidation . ValidateImmutableField ( spec . Completions , oldSpec . Completions , "completions" ) ... )
allErrs = append ( allErrs , apivalidation . ValidateImmutableField ( spec . Selector , oldSpec . Selector , "selector" ) ... )
allErrs = append ( allErrs , apivalidation . ValidateImmutableField ( spec . Template , oldSpec . Template , "template" ) ... )
2015-08-18 14:39:49 +00:00
return allErrs
}
2015-09-22 08:05:54 +00:00
2015-11-04 07:47:11 +00:00
func ValidateJobStatusUpdate ( status , oldStatus extensions . JobStatus ) errs . ValidationErrorList {
2015-09-22 08:05:54 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , ValidateJobStatus ( & status ) ... )
return allErrs
}
2015-09-22 18:26:36 +00:00
2015-09-23 03:20:13 +00:00
// ValidateIngress tests if required fields in the Ingress are set.
2015-10-09 22:49:10 +00:00
func ValidateIngress ( ingress * extensions . Ingress ) errs . ValidationErrorList {
2015-09-23 03:20:13 +00:00
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMeta ( & ingress . ObjectMeta , true , ValidateIngressName ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , ValidateIngressSpec ( & ingress . Spec ) . Prefix ( "spec" ) ... )
return allErrs
}
2015-09-22 18:26:36 +00:00
// ValidateIngressName validates that the given name can be used as an Ingress name.
func ValidateIngressName ( name string , prefix bool ) ( bool , string ) {
return apivalidation . NameIsDNSSubdomain ( name , prefix )
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set.
2015-10-09 22:49:10 +00:00
func ValidateIngressSpec ( spec * extensions . IngressSpec ) errs . ValidationErrorList {
2015-09-22 18:26:36 +00:00
allErrs := errs . ValidationErrorList { }
2015-09-23 03:20:13 +00:00
// TODO: Is a default backend mandatory?
if spec . Backend != nil {
allErrs = append ( allErrs , validateIngressBackend ( spec . Backend ) . Prefix ( "backend" ) ... )
} else if len ( spec . Rules ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "rules" , spec . Rules , "Either a default backend or a set of host rules are required for ingress." ) )
}
if len ( spec . Rules ) > 0 {
allErrs = append ( allErrs , validateIngressRules ( spec . Rules ) . Prefix ( "rules" ) ... )
}
2015-09-22 18:26:36 +00:00
return allErrs
}
2015-09-23 03:20:13 +00:00
// ValidateIngressUpdate tests if required fields in the Ingress are set.
2015-11-04 07:47:11 +00:00
func ValidateIngressUpdate ( ingress , oldIngress * extensions . Ingress ) errs . ValidationErrorList {
2015-09-22 18:26:36 +00:00
allErrs := errs . ValidationErrorList { }
2015-09-23 03:20:13 +00:00
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & ingress . ObjectMeta , & oldIngress . ObjectMeta ) . Prefix ( "metadata" ) ... )
2015-09-22 18:26:36 +00:00
allErrs = append ( allErrs , ValidateIngressSpec ( & ingress . Spec ) . Prefix ( "spec" ) ... )
return allErrs
}
2015-10-12 19:09:20 +00:00
// ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
func ValidateIngressStatusUpdate ( ingress , oldIngress * extensions . Ingress ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & ingress . ObjectMeta , & oldIngress . ObjectMeta ) . Prefix ( "metadata" ) ... )
allErrs = append ( allErrs , apivalidation . ValidateLoadBalancerStatus ( & ingress . Status . LoadBalancer ) . Prefix ( "status.loadBalancer" ) ... )
return allErrs
}
2015-10-09 22:49:10 +00:00
func validateIngressRules ( IngressRules [ ] extensions . IngressRule ) errs . ValidationErrorList {
2015-09-22 18:26:36 +00:00
allErrs := errs . ValidationErrorList { }
2015-09-23 03:20:13 +00:00
if len ( IngressRules ) == 0 {
return append ( allErrs , errs . NewFieldRequired ( "IngressRules" ) )
}
for _ , ih := range IngressRules {
if len ( ih . Host ) > 0 {
// TODO: Ports and ips are allowed in the host part of a url
// according to RFC 3986, consider allowing them.
if valid , errMsg := apivalidation . NameIsDNSSubdomain ( ih . Host , false ) ; ! valid {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "host" , ih . Host , errMsg ) )
}
if isIP := ( net . ParseIP ( ih . Host ) != nil ) ; isIP {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "host" , ih . Host , "Host must be a DNS name, not ip address" ) )
}
}
allErrs = append ( allErrs , validateIngressRuleValue ( & ih . IngressRuleValue ) . Prefix ( "ingressRule" ) ... )
}
return allErrs
}
2015-10-09 22:49:10 +00:00
func validateIngressRuleValue ( ingressRule * extensions . IngressRuleValue ) errs . ValidationErrorList {
2015-09-23 03:20:13 +00:00
allErrs := errs . ValidationErrorList { }
if ingressRule . HTTP != nil {
allErrs = append ( allErrs , validateHTTPIngressRuleValue ( ingressRule . HTTP ) . Prefix ( "http" ) ... )
}
return allErrs
}
2015-10-09 22:49:10 +00:00
func validateHTTPIngressRuleValue ( httpIngressRuleValue * extensions . HTTPIngressRuleValue ) errs . ValidationErrorList {
2015-09-23 03:20:13 +00:00
allErrs := errs . ValidationErrorList { }
if len ( httpIngressRuleValue . Paths ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "paths" ) )
}
for _ , rule := range httpIngressRuleValue . Paths {
if len ( rule . Path ) > 0 {
if ! strings . HasPrefix ( rule . Path , "/" ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "path" , rule . Path , "path must begin with /" ) )
}
// TODO: More draconian path regex validation.
// Path must be a valid regex. This is the basic requirement.
// In addition to this any characters not allowed in a path per
// RFC 3986 section-3.3 cannot appear as a literal in the regex.
// Consider the example: http://host/valid?#bar, everything after
// the last '/' is a valid regex that matches valid#bar, which
// isn't a valid path, because the path terminates at the first ?
// or #. A more sophisticated form of validation would detect that
// the user is confusing url regexes with path regexes.
_ , err := regexp . CompilePOSIX ( rule . Path )
if err != nil {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "path" , rule . Path , "httpIngressRuleValue.path must be a valid regex." ) )
}
}
allErrs = append ( allErrs , validateIngressBackend ( & rule . Backend ) . Prefix ( "backend" ) ... )
}
return allErrs
}
// validateIngressBackend tests if a given backend is valid.
2015-10-09 22:49:10 +00:00
func validateIngressBackend ( backend * extensions . IngressBackend ) errs . ValidationErrorList {
2015-09-23 03:20:13 +00:00
allErrs := errs . ValidationErrorList { }
// All backends must reference a single local service by name, and a single service port by name or number.
if len ( backend . ServiceName ) == 0 {
return append ( allErrs , errs . NewFieldRequired ( "serviceName" ) )
} else if ok , errMsg := apivalidation . ValidateServiceName ( backend . ServiceName , false ) ; ! ok {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "serviceName" , backend . ServiceName , errMsg ) )
}
if backend . ServicePort . Kind == util . IntstrString {
if ! utilvalidation . IsDNS1123Label ( backend . ServicePort . StrVal ) {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "servicePort" , backend . ServicePort . StrVal , apivalidation . DNS1123LabelErrorMsg ) )
}
if ! utilvalidation . IsValidPortName ( backend . ServicePort . StrVal ) {
2015-10-12 03:26:17 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "servicePort" , backend . ServicePort . StrVal , apivalidation . PortNameErrorMsg ) )
2015-09-23 03:20:13 +00:00
}
} else if ! utilvalidation . IsValidPortNum ( backend . ServicePort . IntVal ) {
2015-10-12 03:26:17 +00:00
allErrs = append ( allErrs , errs . NewFieldInvalid ( "servicePort" , backend . ServicePort , apivalidation . PortRangeErrorMsg ) )
2015-09-23 03:20:13 +00:00
}
2015-09-22 18:26:36 +00:00
return allErrs
}
2015-10-05 20:22:01 +00:00
2015-10-09 22:49:10 +00:00
func validateClusterAutoscalerSpec ( spec extensions . ClusterAutoscalerSpec ) errs . ValidationErrorList {
2015-10-05 20:22:01 +00:00
allErrs := errs . ValidationErrorList { }
if spec . MinNodes < 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "minNodes" , spec . MinNodes , ` must be non-negative ` ) )
}
if spec . MaxNodes < spec . MinNodes {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "maxNodes" , spec . MaxNodes , ` must be bigger or equal to minNodes ` ) )
}
if len ( spec . TargetUtilization ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "targetUtilization" ) )
}
for _ , target := range spec . TargetUtilization {
if len ( target . Resource ) == 0 {
allErrs = append ( allErrs , errs . NewFieldRequired ( "targetUtilization.resource" ) )
}
if target . Value <= 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "targetUtilization.value" , target . Value , "must be greater than 0" ) )
}
if target . Value > 1 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "targetUtilization.value" , target . Value , "must be less or equal 1" ) )
}
}
return allErrs
}
2015-10-09 22:49:10 +00:00
func ValidateClusterAutoscaler ( autoscaler * extensions . ClusterAutoscaler ) errs . ValidationErrorList {
2015-10-05 20:22:01 +00:00
allErrs := errs . ValidationErrorList { }
if autoscaler . Name != "ClusterAutoscaler" {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "name" , autoscaler . Name , ` name must be ClusterAutoscaler ` ) )
}
if autoscaler . Namespace != api . NamespaceDefault {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "namespace" , autoscaler . Namespace , ` namespace must be default ` ) )
}
allErrs = append ( allErrs , validateClusterAutoscalerSpec ( autoscaler . Spec ) ... )
return allErrs
}
2015-10-14 18:03:59 +00:00
func ValidatePodSelector ( ps * extensions . PodSelector ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
if ps == nil {
return allErrs
}
allErrs = append ( allErrs , apivalidation . ValidateLabels ( ps . MatchLabels , "matchLabels" ) ... )
for i , expr := range ps . MatchExpressions {
allErrs = append ( allErrs , ValidatePodSelectorRequirement ( expr ) . Prefix ( fmt . Sprintf ( "matchExpressions.[%v]" , i ) ) ... )
}
return allErrs
}
func ValidatePodSelectorRequirement ( sr extensions . PodSelectorRequirement ) errs . ValidationErrorList {
allErrs := errs . ValidationErrorList { }
switch sr . Operator {
case extensions . PodSelectorOpIn , extensions . PodSelectorOpNotIn :
if len ( sr . Values ) == 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "values" , sr . Values , "must be non-empty when operator is In or NotIn" ) )
}
case extensions . PodSelectorOpExists , extensions . PodSelectorOpDoesNotExist :
if len ( sr . Values ) > 0 {
allErrs = append ( allErrs , errs . NewFieldInvalid ( "values" , sr . Values , "must be empty when operator is Exists or DoesNotExist" ) )
}
default :
allErrs = append ( allErrs , errs . NewFieldInvalid ( "operator" , sr . Operator , "not a valid pod selector operator" ) )
}
allErrs = append ( allErrs , apivalidation . ValidateLabelName ( sr . Key , "key" ) ... )
return allErrs
}