2019-01-12 04:58:27 +00:00
/ *
Copyright 2016 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 validation
import (
"fmt"
"strconv"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
)
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateStatefulSetName ( name string , prefix bool ) [ ] string {
// TODO: Validate that there's name for the suffix inserted by the pods.
// Currently this is just "-index". In the future we may allow a user
// specified list of suffixes and we need to validate the longest one.
return apimachineryvalidation . NameIsDNSSubdomain ( name , prefix )
}
2019-04-07 17:07:55 +00:00
// ValidatePodTemplateSpecForStatefulSet validates the given template and ensures that it is in accordance with the desired selector.
2019-01-12 04:58:27 +00:00
func ValidatePodTemplateSpecForStatefulSet ( template * api . PodTemplateSpec , selector labels . Selector , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if template == nil {
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
} else {
if ! selector . Empty ( ) {
// Verify that the StatefulSet selector matches the labels in template.
labels := labels . Set ( template . Labels )
if ! selector . Matches ( labels ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "metadata" , "labels" ) , template . Labels , "`selector` does not match template `labels`" ) )
}
}
// TODO: Add validation for PodSpec, currently this will check volumes, which we know will
// fail. We should really check that the union of the given volumes and volumeClaims match
// volume mounts in the containers.
// allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...)
allErrs = append ( allErrs , unversionedvalidation . ValidateLabels ( template . Labels , fldPath . Child ( "labels" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateAnnotations ( template . Annotations , fldPath . Child ( "annotations" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidatePodSpecificAnnotations ( template . Annotations , & template . Spec , fldPath . Child ( "annotations" ) ) ... )
}
return allErrs
}
// ValidateStatefulSetSpec tests if required fields in the StatefulSet spec are set.
func ValidateStatefulSetSpec ( spec * apps . StatefulSetSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
switch spec . PodManagementPolicy {
case "" :
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "podManagementPolicy" ) , "" ) )
case apps . OrderedReadyPodManagement , apps . ParallelPodManagement :
default :
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "podManagementPolicy" ) , spec . PodManagementPolicy , fmt . Sprintf ( "must be '%s' or '%s'" , apps . OrderedReadyPodManagement , apps . ParallelPodManagement ) ) )
}
switch spec . UpdateStrategy . Type {
case "" :
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "updateStrategy" ) , "" ) )
case apps . OnDeleteStatefulSetStrategyType :
if spec . UpdateStrategy . RollingUpdate != nil {
allErrs = append (
allErrs ,
field . Invalid (
fldPath . Child ( "updateStrategy" ) . Child ( "rollingUpdate" ) ,
spec . UpdateStrategy . RollingUpdate ,
fmt . Sprintf ( "only allowed for updateStrategy '%s'" , apps . RollingUpdateStatefulSetStrategyType ) ) )
}
case apps . RollingUpdateStatefulSetStrategyType :
if spec . UpdateStrategy . RollingUpdate != nil {
allErrs = append ( allErrs ,
apivalidation . ValidateNonnegativeField (
int64 ( spec . UpdateStrategy . RollingUpdate . Partition ) ,
fldPath . Child ( "updateStrategy" ) . Child ( "rollingUpdate" ) . Child ( "partition" ) ) ... )
}
default :
allErrs = append ( allErrs ,
field . Invalid ( fldPath . Child ( "updateStrategy" ) , spec . UpdateStrategy ,
fmt . Sprintf ( "must be '%s' or '%s'" ,
apps . RollingUpdateStatefulSetStrategyType ,
apps . OnDeleteStatefulSetStrategyType ) ) )
}
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
if spec . Selector == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "selector" ) , "" ) )
} else {
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
if len ( spec . Selector . MatchLabels ) + len ( spec . Selector . MatchExpressions ) == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "empty selector is invalid for statefulset" ) )
}
}
selector , err := metav1 . LabelSelectorAsSelector ( spec . Selector )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "" ) )
} else {
allErrs = append ( allErrs , ValidatePodTemplateSpecForStatefulSet ( & spec . Template , selector , fldPath . Child ( "template" ) ) ... )
}
if spec . Template . Spec . RestartPolicy != api . RestartPolicyAlways {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "template" , "spec" , "restartPolicy" ) , spec . Template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyAlways ) } ) )
}
if spec . Template . Spec . ActiveDeadlineSeconds != nil {
2019-04-07 17:07:55 +00:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "template" , "spec" , "activeDeadlineSeconds" ) , "activeDeadlineSeconds in StatefulSet is not Supported" ) )
2019-01-12 04:58:27 +00:00
}
return allErrs
}
// ValidateStatefulSet validates a StatefulSet.
func ValidateStatefulSet ( statefulSet * apps . StatefulSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMeta ( & statefulSet . ObjectMeta , true , ValidateStatefulSetName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateStatefulSetSpec ( & statefulSet . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
// ValidateStatefulSetUpdate tests if required fields in the StatefulSet are set.
func ValidateStatefulSetUpdate ( statefulSet , oldStatefulSet * apps . StatefulSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & statefulSet . ObjectMeta , & oldStatefulSet . ObjectMeta , field . NewPath ( "metadata" ) )
restoreReplicas := statefulSet . Spec . Replicas
statefulSet . Spec . Replicas = oldStatefulSet . Spec . Replicas
restoreTemplate := statefulSet . Spec . Template
statefulSet . Spec . Template = oldStatefulSet . Spec . Template
restoreStrategy := statefulSet . Spec . UpdateStrategy
statefulSet . Spec . UpdateStrategy = oldStatefulSet . Spec . UpdateStrategy
if ! apiequality . Semantic . DeepEqual ( statefulSet . Spec , oldStatefulSet . Spec ) {
allErrs = append ( allErrs , field . Forbidden ( field . NewPath ( "spec" ) , "updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden" ) )
}
statefulSet . Spec . Replicas = restoreReplicas
statefulSet . Spec . Template = restoreTemplate
statefulSet . Spec . UpdateStrategy = restoreStrategy
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( statefulSet . Spec . Replicas ) , field . NewPath ( "spec" , "replicas" ) ) ... )
return allErrs
}
// ValidateStatefulSetStatus validates a StatefulSetStatus.
func ValidateStatefulSetStatus ( status * apps . StatefulSetStatus , fieldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . Replicas ) , fieldPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . ReadyReplicas ) , fieldPath . Child ( "readyReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . CurrentReplicas ) , fieldPath . Child ( "currentReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . UpdatedReplicas ) , fieldPath . Child ( "updatedReplicas" ) ) ... )
if status . ObservedGeneration != nil {
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * status . ObservedGeneration ) , fieldPath . Child ( "observedGeneration" ) ) ... )
}
if status . CollisionCount != nil {
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * status . CollisionCount ) , fieldPath . Child ( "collisionCount" ) ) ... )
}
msg := "cannot be greater than status.replicas"
if status . ReadyReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fieldPath . Child ( "readyReplicas" ) , status . ReadyReplicas , msg ) )
}
if status . CurrentReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fieldPath . Child ( "currentReplicas" ) , status . CurrentReplicas , msg ) )
}
if status . UpdatedReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fieldPath . Child ( "updatedReplicas" ) , status . UpdatedReplicas , msg ) )
}
return allErrs
}
// ValidateStatefulSetStatusUpdate tests if required fields in the StatefulSet are set.
func ValidateStatefulSetStatusUpdate ( statefulSet , oldStatefulSet * apps . StatefulSet ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidateStatefulSetStatus ( & statefulSet . Status , field . NewPath ( "status" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & statefulSet . ObjectMeta , & oldStatefulSet . ObjectMeta , field . NewPath ( "metadata" ) ) ... )
// TODO: Validate status.
if apivalidation . IsDecremented ( statefulSet . Status . CollisionCount , oldStatefulSet . Status . CollisionCount ) {
value := int32 ( 0 )
if statefulSet . Status . CollisionCount != nil {
value = * statefulSet . Status . CollisionCount
}
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "status" ) . Child ( "collisionCount" ) , value , "cannot be decremented" ) )
}
return allErrs
}
// ValidateControllerRevisionName can be used to check whether the given ControllerRevision name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateControllerRevisionName = apimachineryvalidation . NameIsDNSSubdomain
// ValidateControllerRevision collects errors for the fields of state and returns those errors as an ErrorList. If the
// returned list is empty, state is valid. Validation is performed to ensure that state is a valid ObjectMeta, its name
// is valid, and that it doesn't exceed the MaxControllerRevisionSize.
func ValidateControllerRevision ( revision * apps . ControllerRevision ) field . ErrorList {
errs := field . ErrorList { }
errs = append ( errs , apivalidation . ValidateObjectMeta ( & revision . ObjectMeta , true , ValidateControllerRevisionName , field . NewPath ( "metadata" ) ) ... )
if revision . Data == nil {
errs = append ( errs , field . Required ( field . NewPath ( "data" ) , "data is mandatory" ) )
}
errs = append ( errs , apivalidation . ValidateNonnegativeField ( revision . Revision , field . NewPath ( "revision" ) ) ... )
return errs
}
// ValidateControllerRevisionUpdate collects errors pertaining to the mutation of an ControllerRevision Object. If the
// returned ErrorList is empty the update operation is valid. Any mutation to the ControllerRevision's Data or Revision
// is considered to be invalid.
func ValidateControllerRevisionUpdate ( newHistory , oldHistory * apps . ControllerRevision ) field . ErrorList {
errs := field . ErrorList { }
errs = append ( errs , apivalidation . ValidateObjectMetaUpdate ( & newHistory . ObjectMeta , & oldHistory . ObjectMeta , field . NewPath ( "metadata" ) ) ... )
errs = append ( errs , ValidateControllerRevision ( newHistory ) ... )
errs = append ( errs , apivalidation . ValidateImmutableField ( newHistory . Data , oldHistory . Data , field . NewPath ( "data" ) ) ... )
return errs
}
// ValidateDaemonSet tests if required fields in the DaemonSet are set.
func ValidateDaemonSet ( ds * apps . DaemonSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMeta ( & ds . ObjectMeta , true , ValidateDaemonSetName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateDaemonSetSpec ( & ds . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
func ValidateDaemonSetUpdate ( ds , oldDS * apps . DaemonSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & ds . ObjectMeta , & oldDS . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateDaemonSetSpecUpdate ( & ds . Spec , & oldDS . Spec , field . NewPath ( "spec" ) ) ... )
allErrs = append ( allErrs , ValidateDaemonSetSpec ( & ds . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
func ValidateDaemonSetSpecUpdate ( newSpec , oldSpec * apps . DaemonSetSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
// TemplateGeneration shouldn't be decremented
if newSpec . TemplateGeneration < oldSpec . TemplateGeneration {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "templateGeneration" ) , newSpec . TemplateGeneration , "must not be decremented" ) )
}
// TemplateGeneration should be increased when and only when template is changed
templateUpdated := ! apiequality . Semantic . DeepEqual ( newSpec . Template , oldSpec . Template )
if newSpec . TemplateGeneration == oldSpec . TemplateGeneration && templateUpdated {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "templateGeneration" ) , newSpec . TemplateGeneration , "must be incremented upon template update" ) )
} else if newSpec . TemplateGeneration > oldSpec . TemplateGeneration && ! templateUpdated {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "templateGeneration" ) , newSpec . TemplateGeneration , "must not be incremented without template update" ) )
}
return allErrs
}
// validateDaemonSetStatus validates a DaemonSetStatus
func validateDaemonSetStatus ( status * apps . DaemonSetStatus , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . CurrentNumberScheduled ) , fldPath . Child ( "currentNumberScheduled" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . NumberMisscheduled ) , fldPath . Child ( "numberMisscheduled" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . DesiredNumberScheduled ) , fldPath . Child ( "desiredNumberScheduled" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . NumberReady ) , fldPath . Child ( "numberReady" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( status . ObservedGeneration , fldPath . Child ( "observedGeneration" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . UpdatedNumberScheduled ) , fldPath . Child ( "updatedNumberScheduled" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . NumberAvailable ) , fldPath . Child ( "numberAvailable" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . NumberUnavailable ) , fldPath . Child ( "numberUnavailable" ) ) ... )
if status . CollisionCount != nil {
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * status . CollisionCount ) , fldPath . Child ( "collisionCount" ) ) ... )
}
return allErrs
}
2019-04-07 17:07:55 +00:00
// ValidateDaemonSetStatusUpdate validates tests if required fields in the DaemonSet Status section
2019-01-12 04:58:27 +00:00
func ValidateDaemonSetStatusUpdate ( ds , oldDS * apps . DaemonSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & ds . ObjectMeta , & oldDS . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , validateDaemonSetStatus ( & ds . Status , field . NewPath ( "status" ) ) ... )
if apivalidation . IsDecremented ( ds . Status . CollisionCount , oldDS . Status . CollisionCount ) {
value := int32 ( 0 )
if ds . Status . CollisionCount != nil {
value = * ds . Status . CollisionCount
}
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "status" ) . Child ( "collisionCount" ) , value , "cannot be decremented" ) )
}
return allErrs
}
// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
func ValidateDaemonSetSpec ( spec * apps . DaemonSetSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
selector , err := metav1 . LabelSelectorAsSelector ( spec . Selector )
if err == nil && ! selector . Matches ( labels . Set ( spec . Template . Labels ) ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "template" , "metadata" , "labels" ) , spec . Template . Labels , "`selector` does not match template `labels`" ) )
}
if spec . Selector != nil && len ( spec . Selector . MatchLabels ) + len ( spec . Selector . MatchExpressions ) == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "empty selector is invalid for daemonset" ) )
}
allErrs = append ( allErrs , apivalidation . ValidatePodTemplateSpec ( & spec . Template , fldPath . Child ( "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 , fldPath . Child ( "template" , "spec" , "volumes" ) ) ... )
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
if spec . Template . Spec . RestartPolicy != api . RestartPolicyAlways {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "template" , "spec" , "restartPolicy" ) , spec . Template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyAlways ) } ) )
}
if spec . Template . Spec . ActiveDeadlineSeconds != nil {
2019-04-07 17:07:55 +00:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "template" , "spec" , "activeDeadlineSeconds" ) , "activeDeadlineSeconds in DaemonSet is not Supported" ) )
2019-01-12 04:58:27 +00:00
}
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . MinReadySeconds ) , fldPath . Child ( "minReadySeconds" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . TemplateGeneration ) , fldPath . Child ( "templateGeneration" ) ) ... )
allErrs = append ( allErrs , ValidateDaemonSetUpdateStrategy ( & spec . UpdateStrategy , fldPath . Child ( "updateStrategy" ) ) ... )
if spec . RevisionHistoryLimit != nil {
// zero is a valid RevisionHistoryLimit
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * spec . RevisionHistoryLimit ) , fldPath . Child ( "revisionHistoryLimit" ) ) ... )
}
return allErrs
}
func ValidateRollingUpdateDaemonSet ( rollingUpdate * apps . RollingUpdateDaemonSet , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidatePositiveIntOrPercent ( rollingUpdate . MaxUnavailable , fldPath . Child ( "maxUnavailable" ) ) ... )
if getIntOrPercentValue ( rollingUpdate . MaxUnavailable ) == 0 {
// MaxUnavailable cannot be 0.
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "maxUnavailable" ) , rollingUpdate . MaxUnavailable , "cannot be 0" ) )
}
// Validate that MaxUnavailable is not more than 100%.
allErrs = append ( allErrs , IsNotMoreThan100Percent ( rollingUpdate . MaxUnavailable , fldPath . Child ( "maxUnavailable" ) ) ... )
return allErrs
}
func ValidateDaemonSetUpdateStrategy ( strategy * apps . DaemonSetUpdateStrategy , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
switch strategy . Type {
case apps . OnDeleteDaemonSetStrategyType :
case apps . RollingUpdateDaemonSetStrategyType :
// Make sure RollingUpdate field isn't nil.
if strategy . RollingUpdate == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "rollingUpdate" ) , "" ) )
return allErrs
}
allErrs = append ( allErrs , ValidateRollingUpdateDaemonSet ( strategy . RollingUpdate , fldPath . Child ( "rollingUpdate" ) ) ... )
default :
validValues := [ ] string { string ( apps . RollingUpdateDaemonSetStrategyType ) , string ( apps . OnDeleteDaemonSetStrategyType ) }
allErrs = append ( allErrs , field . NotSupported ( fldPath , strategy , validValues ) )
}
return allErrs
}
// ValidateDaemonSetName can be used to check whether the given daemon set name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateDaemonSetName = apimachineryvalidation . NameIsDNSSubdomain
2019-04-07 17:07:55 +00:00
// ValidateDeploymentName validates that the given name can be used as a deployment name.
2019-01-12 04:58:27 +00:00
var ValidateDeploymentName = apimachineryvalidation . NameIsDNSSubdomain
func ValidatePositiveIntOrPercent ( intOrPercent intstr . IntOrString , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
switch intOrPercent . Type {
case intstr . String :
for _ , msg := range validation . IsValidPercent ( intOrPercent . StrVal ) {
allErrs = append ( allErrs , field . Invalid ( fldPath , intOrPercent , msg ) )
}
case intstr . Int :
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( intOrPercent . IntValue ( ) ) , fldPath ) ... )
default :
allErrs = append ( allErrs , field . Invalid ( fldPath , intOrPercent , "must be an integer or percentage (e.g '5%%')" ) )
}
return allErrs
}
func getPercentValue ( intOrStringValue intstr . IntOrString ) ( int , bool ) {
if intOrStringValue . Type != intstr . String {
return 0 , false
}
if len ( validation . IsValidPercent ( intOrStringValue . StrVal ) ) != 0 {
return 0 , false
}
value , _ := strconv . Atoi ( intOrStringValue . StrVal [ : len ( intOrStringValue . StrVal ) - 1 ] )
return value , true
}
func getIntOrPercentValue ( intOrStringValue intstr . IntOrString ) int {
value , isPercent := getPercentValue ( intOrStringValue )
if isPercent {
return value
}
return intOrStringValue . IntValue ( )
}
func IsNotMoreThan100Percent ( intOrStringValue intstr . IntOrString , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
value , isPercent := getPercentValue ( intOrStringValue )
if ! isPercent || value <= 100 {
return nil
}
allErrs = append ( allErrs , field . Invalid ( fldPath , intOrStringValue , "must not be greater than 100%" ) )
return allErrs
}
func ValidateRollingUpdateDeployment ( rollingUpdate * apps . RollingUpdateDeployment , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , ValidatePositiveIntOrPercent ( rollingUpdate . MaxUnavailable , fldPath . Child ( "maxUnavailable" ) ) ... )
allErrs = append ( allErrs , ValidatePositiveIntOrPercent ( rollingUpdate . MaxSurge , fldPath . Child ( "maxSurge" ) ) ... )
if getIntOrPercentValue ( rollingUpdate . MaxUnavailable ) == 0 && getIntOrPercentValue ( rollingUpdate . MaxSurge ) == 0 {
// Both MaxSurge and MaxUnavailable cannot be zero.
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "maxUnavailable" ) , rollingUpdate . MaxUnavailable , "may not be 0 when `maxSurge` is 0" ) )
}
// Validate that MaxUnavailable is not more than 100%.
allErrs = append ( allErrs , IsNotMoreThan100Percent ( rollingUpdate . MaxUnavailable , fldPath . Child ( "maxUnavailable" ) ) ... )
return allErrs
}
func ValidateDeploymentStrategy ( strategy * apps . DeploymentStrategy , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
switch strategy . Type {
case apps . RecreateDeploymentStrategyType :
if strategy . RollingUpdate != nil {
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "rollingUpdate" ) , "may not be specified when strategy `type` is '" + string ( apps . RecreateDeploymentStrategyType + "'" ) ) )
}
case apps . RollingUpdateDeploymentStrategyType :
// This should never happen since it's set and checked in defaults.go
if strategy . RollingUpdate == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "rollingUpdate" ) , "this should be defaulted and never be nil" ) )
} else {
allErrs = append ( allErrs , ValidateRollingUpdateDeployment ( strategy . RollingUpdate , fldPath . Child ( "rollingUpdate" ) ) ... )
}
default :
validValues := [ ] string { string ( apps . RecreateDeploymentStrategyType ) , string ( apps . RollingUpdateDeploymentStrategyType ) }
allErrs = append ( allErrs , field . NotSupported ( fldPath , strategy , validValues ) )
}
return allErrs
}
func ValidateRollback ( rollback * apps . RollbackConfig , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
v := rollback . Revision
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( v ) , fldPath . Child ( "version" ) ) ... )
return allErrs
}
2019-04-07 17:07:55 +00:00
// ValidateDeploymentSpec validates given deployment spec.
2019-01-12 04:58:27 +00:00
func ValidateDeploymentSpec ( spec * apps . DeploymentSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
if spec . Selector == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "selector" ) , "" ) )
} else {
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
if len ( spec . Selector . MatchLabels ) + len ( spec . Selector . MatchExpressions ) == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "empty selector is invalid for deployment" ) )
}
}
selector , err := metav1 . LabelSelectorAsSelector ( spec . Selector )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "invalid label selector" ) )
} else {
allErrs = append ( allErrs , ValidatePodTemplateSpecForReplicaSet ( & spec . Template , selector , spec . Replicas , fldPath . Child ( "template" ) ) ... )
}
allErrs = append ( allErrs , ValidateDeploymentStrategy ( & spec . Strategy , fldPath . Child ( "strategy" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . MinReadySeconds ) , fldPath . Child ( "minReadySeconds" ) ) ... )
if spec . RevisionHistoryLimit != nil {
// zero is a valid RevisionHistoryLimit
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * spec . RevisionHistoryLimit ) , fldPath . Child ( "revisionHistoryLimit" ) ) ... )
}
if spec . RollbackTo != nil {
allErrs = append ( allErrs , ValidateRollback ( spec . RollbackTo , fldPath . Child ( "rollback" ) ) ... )
}
if spec . ProgressDeadlineSeconds != nil {
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * spec . ProgressDeadlineSeconds ) , fldPath . Child ( "progressDeadlineSeconds" ) ) ... )
if * spec . ProgressDeadlineSeconds <= spec . MinReadySeconds {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "progressDeadlineSeconds" ) , spec . ProgressDeadlineSeconds , "must be greater than minReadySeconds" ) )
}
}
return allErrs
}
2019-04-07 17:07:55 +00:00
// ValidateDeploymentStatus validates given deployment status.
2019-01-12 04:58:27 +00:00
func ValidateDeploymentStatus ( status * apps . DeploymentStatus , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( status . ObservedGeneration , fldPath . Child ( "observedGeneration" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . UpdatedReplicas ) , fldPath . Child ( "updatedReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . ReadyReplicas ) , fldPath . Child ( "readyReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . AvailableReplicas ) , fldPath . Child ( "availableReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . UnavailableReplicas ) , fldPath . Child ( "unavailableReplicas" ) ) ... )
if status . CollisionCount != nil {
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( * status . CollisionCount ) , fldPath . Child ( "collisionCount" ) ) ... )
}
msg := "cannot be greater than status.replicas"
if status . UpdatedReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "updatedReplicas" ) , status . UpdatedReplicas , msg ) )
}
if status . ReadyReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "readyReplicas" ) , status . ReadyReplicas , msg ) )
}
if status . AvailableReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "availableReplicas" ) , status . AvailableReplicas , msg ) )
}
if status . AvailableReplicas > status . ReadyReplicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "availableReplicas" ) , status . AvailableReplicas , "cannot be greater than readyReplicas" ) )
}
return allErrs
}
func ValidateDeploymentUpdate ( update , old * apps . Deployment ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & update . ObjectMeta , & old . ObjectMeta , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateDeploymentSpec ( & update . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
func ValidateDeploymentStatusUpdate ( update , old * apps . Deployment ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMetaUpdate ( & update . ObjectMeta , & old . ObjectMeta , field . NewPath ( "metadata" ) )
fldPath := field . NewPath ( "status" )
allErrs = append ( allErrs , ValidateDeploymentStatus ( & update . Status , fldPath ) ... )
if apivalidation . IsDecremented ( update . Status . CollisionCount , old . Status . CollisionCount ) {
value := int32 ( 0 )
if update . Status . CollisionCount != nil {
value = * update . Status . CollisionCount
}
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "collisionCount" ) , value , "cannot be decremented" ) )
}
return allErrs
}
func ValidateDeployment ( obj * apps . Deployment ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMeta ( & obj . ObjectMeta , true , ValidateDeploymentName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateDeploymentSpec ( & obj . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
func ValidateDeploymentRollback ( obj * apps . DeploymentRollback ) field . ErrorList {
allErrs := apivalidation . ValidateAnnotations ( obj . UpdatedAnnotations , field . NewPath ( "updatedAnnotations" ) )
if len ( obj . Name ) == 0 {
allErrs = append ( allErrs , field . Required ( field . NewPath ( "name" ) , "name is required" ) )
}
allErrs = append ( allErrs , ValidateRollback ( & obj . RollbackTo , field . NewPath ( "rollback" ) ) ... )
return allErrs
}
// ValidateReplicaSetName can be used to check whether the given ReplicaSet
// name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateReplicaSetName = apimachineryvalidation . NameIsDNSSubdomain
// ValidateReplicaSet tests if required fields in the ReplicaSet are set.
func ValidateReplicaSet ( rs * apps . ReplicaSet ) field . ErrorList {
allErrs := apivalidation . ValidateObjectMeta ( & rs . ObjectMeta , true , ValidateReplicaSetName , field . NewPath ( "metadata" ) )
allErrs = append ( allErrs , ValidateReplicaSetSpec ( & rs . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
// ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
func ValidateReplicaSetUpdate ( rs , oldRs * apps . ReplicaSet ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & rs . ObjectMeta , & oldRs . ObjectMeta , field . NewPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , ValidateReplicaSetSpec ( & rs . Spec , field . NewPath ( "spec" ) ) ... )
return allErrs
}
// ValidateReplicaSetStatusUpdate tests if required fields in the ReplicaSet are set.
func ValidateReplicaSetStatusUpdate ( rs , oldRs * apps . ReplicaSet ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateObjectMetaUpdate ( & rs . ObjectMeta , & oldRs . ObjectMeta , field . NewPath ( "metadata" ) ) ... )
allErrs = append ( allErrs , ValidateReplicaSetStatus ( rs . Status , field . NewPath ( "status" ) ) ... )
return allErrs
}
func ValidateReplicaSetStatus ( status apps . ReplicaSetStatus , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . FullyLabeledReplicas ) , fldPath . Child ( "fullyLabeledReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . ReadyReplicas ) , fldPath . Child ( "readyReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . AvailableReplicas ) , fldPath . Child ( "availableReplicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( status . ObservedGeneration ) , fldPath . Child ( "observedGeneration" ) ) ... )
msg := "cannot be greater than status.replicas"
if status . FullyLabeledReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "fullyLabeledReplicas" ) , status . FullyLabeledReplicas , msg ) )
}
if status . ReadyReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "readyReplicas" ) , status . ReadyReplicas , msg ) )
}
if status . AvailableReplicas > status . Replicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "availableReplicas" ) , status . AvailableReplicas , msg ) )
}
if status . AvailableReplicas > status . ReadyReplicas {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "availableReplicas" ) , status . AvailableReplicas , "cannot be greater than readyReplicas" ) )
}
return allErrs
}
// ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
func ValidateReplicaSetSpec ( spec * apps . ReplicaSetSpec , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . Replicas ) , fldPath . Child ( "replicas" ) ) ... )
allErrs = append ( allErrs , apivalidation . ValidateNonnegativeField ( int64 ( spec . MinReadySeconds ) , fldPath . Child ( "minReadySeconds" ) ) ... )
if spec . Selector == nil {
allErrs = append ( allErrs , field . Required ( fldPath . Child ( "selector" ) , "" ) )
} else {
allErrs = append ( allErrs , unversionedvalidation . ValidateLabelSelector ( spec . Selector , fldPath . Child ( "selector" ) ) ... )
if len ( spec . Selector . MatchLabels ) + len ( spec . Selector . MatchExpressions ) == 0 {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "empty selector is invalid for deployment" ) )
}
}
selector , err := metav1 . LabelSelectorAsSelector ( spec . Selector )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "selector" ) , spec . Selector , "invalid label selector" ) )
} else {
allErrs = append ( allErrs , ValidatePodTemplateSpecForReplicaSet ( & spec . Template , selector , spec . Replicas , fldPath . Child ( "template" ) ) ... )
}
return allErrs
}
2019-04-07 17:07:55 +00:00
// ValidatePodTemplateSpecForReplicaSet validates the given template and ensures that it is in accordance with the desired selector and replicas.
2019-01-12 04:58:27 +00:00
func ValidatePodTemplateSpecForReplicaSet ( template * api . PodTemplateSpec , selector labels . Selector , replicas int32 , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
if template == nil {
allErrs = append ( allErrs , field . Required ( fldPath , "" ) )
} else {
if ! selector . Empty ( ) {
// Verify that the ReplicaSet selector matches the labels in template.
labels := labels . Set ( template . Labels )
if ! selector . Matches ( labels ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "metadata" , "labels" ) , template . Labels , "`selector` does not match template `labels`" ) )
}
}
allErrs = append ( allErrs , apivalidation . ValidatePodTemplateSpec ( template , fldPath ) ... )
if replicas > 1 {
allErrs = append ( allErrs , apivalidation . ValidateReadOnlyPersistentDisks ( template . Spec . Volumes , fldPath . Child ( "spec" , "volumes" ) ) ... )
}
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
if template . Spec . RestartPolicy != api . RestartPolicyAlways {
allErrs = append ( allErrs , field . NotSupported ( fldPath . Child ( "spec" , "restartPolicy" ) , template . Spec . RestartPolicy , [ ] string { string ( api . RestartPolicyAlways ) } ) )
}
if template . Spec . ActiveDeadlineSeconds != nil {
2019-04-07 17:07:55 +00:00
allErrs = append ( allErrs , field . Forbidden ( fldPath . Child ( "spec" , "activeDeadlineSeconds" ) , "activeDeadlineSeconds in ReplicaSet is not Supported" ) )
2019-01-12 04:58:27 +00:00
}
}
return allErrs
}