...

Source file src/k8s.io/kubernetes/pkg/apis/apps/validation/validation.go

Documentation: k8s.io/kubernetes/pkg/apis/apps/validation

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validation
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  
    23  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    24  	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	"k8s.io/apimachinery/pkg/util/intstr"
    29  	"k8s.io/apimachinery/pkg/util/validation"
    30  	"k8s.io/apimachinery/pkg/util/validation/field"
    31  	"k8s.io/kubernetes/pkg/apis/apps"
    32  	api "k8s.io/kubernetes/pkg/apis/core"
    33  	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
    34  )
    35  
    36  // ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
    37  // Prefix indicates this name will be used as part of generation, in which case
    38  // trailing dashes are allowed.
    39  func ValidateStatefulSetName(name string, prefix bool) []string {
    40  	// TODO: Validate that there's room for the suffix inserted by the pods.
    41  	// Currently this is just "-index". In the future we may allow a user
    42  	// specified list of suffixes and we need  to validate the longest one.
    43  	return apimachineryvalidation.NameIsDNSLabel(name, prefix)
    44  }
    45  
    46  // ValidatePodTemplateSpecForStatefulSet validates the given template and ensures that it is in accordance with the desired selector.
    47  func ValidatePodTemplateSpecForStatefulSet(template *api.PodTemplateSpec, selector labels.Selector, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
    48  	allErrs := field.ErrorList{}
    49  	if template == nil {
    50  		allErrs = append(allErrs, field.Required(fldPath, ""))
    51  	} else {
    52  		if !selector.Empty() {
    53  			// Verify that the StatefulSet selector matches the labels in template.
    54  			labels := labels.Set(template.Labels)
    55  			if !selector.Matches(labels) {
    56  				allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
    57  			}
    58  		}
    59  		// TODO: Add validation for PodSpec, currently this will check volumes, which we know will
    60  		// fail. We should really check that the union of the given volumes and volumeClaims match
    61  		// volume mounts in the containers.
    62  		// allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...)
    63  		allErrs = append(allErrs, unversionedvalidation.ValidateLabels(template.Labels, fldPath.Child("labels"))...)
    64  		allErrs = append(allErrs, apivalidation.ValidateAnnotations(template.Annotations, fldPath.Child("annotations"))...)
    65  		allErrs = append(allErrs, apivalidation.ValidatePodSpecificAnnotations(template.Annotations, &template.Spec, fldPath.Child("annotations"), opts)...)
    66  	}
    67  	return allErrs
    68  }
    69  
    70  func ValidatePersistentVolumeClaimRetentionPolicyType(policy apps.PersistentVolumeClaimRetentionPolicyType, fldPath *field.Path) field.ErrorList {
    71  	var allErrs field.ErrorList
    72  	switch policy {
    73  	case apps.RetainPersistentVolumeClaimRetentionPolicyType:
    74  	case apps.DeletePersistentVolumeClaimRetentionPolicyType:
    75  	default:
    76  		allErrs = append(allErrs, field.NotSupported(fldPath, policy, []string{string(apps.RetainPersistentVolumeClaimRetentionPolicyType), string(apps.DeletePersistentVolumeClaimRetentionPolicyType)}))
    77  	}
    78  	return allErrs
    79  }
    80  
    81  func ValidatePersistentVolumeClaimRetentionPolicy(policy *apps.StatefulSetPersistentVolumeClaimRetentionPolicy, fldPath *field.Path) field.ErrorList {
    82  	var allErrs field.ErrorList
    83  	if policy != nil {
    84  		allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicyType(policy.WhenDeleted, fldPath.Child("whenDeleted"))...)
    85  		allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicyType(policy.WhenScaled, fldPath.Child("whenScaled"))...)
    86  	}
    87  	return allErrs
    88  }
    89  
    90  // ValidateStatefulSetSpec tests if required fields in the StatefulSet spec are set.
    91  func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
    92  	allErrs := field.ErrorList{}
    93  
    94  	switch spec.PodManagementPolicy {
    95  	case "":
    96  		allErrs = append(allErrs, field.Required(fldPath.Child("podManagementPolicy"), ""))
    97  	case apps.OrderedReadyPodManagement, apps.ParallelPodManagement:
    98  	default:
    99  		allErrs = append(allErrs, field.Invalid(fldPath.Child("podManagementPolicy"), spec.PodManagementPolicy, fmt.Sprintf("must be '%s' or '%s'", apps.OrderedReadyPodManagement, apps.ParallelPodManagement)))
   100  	}
   101  
   102  	switch spec.UpdateStrategy.Type {
   103  	case "":
   104  		allErrs = append(allErrs, field.Required(fldPath.Child("updateStrategy"), ""))
   105  	case apps.OnDeleteStatefulSetStrategyType:
   106  		if spec.UpdateStrategy.RollingUpdate != nil {
   107  			allErrs = append(
   108  				allErrs,
   109  				field.Invalid(
   110  					fldPath.Child("updateStrategy").Child("rollingUpdate"),
   111  					spec.UpdateStrategy.RollingUpdate,
   112  					fmt.Sprintf("only allowed for updateStrategy '%s'", apps.RollingUpdateStatefulSetStrategyType)))
   113  		}
   114  	case apps.RollingUpdateStatefulSetStrategyType:
   115  		if spec.UpdateStrategy.RollingUpdate != nil {
   116  			allErrs = append(allErrs, validateRollingUpdateStatefulSet(spec.UpdateStrategy.RollingUpdate, fldPath.Child("updateStrategy", "rollingUpdate"))...)
   117  
   118  		}
   119  	default:
   120  		allErrs = append(allErrs,
   121  			field.Invalid(fldPath.Child("updateStrategy"), spec.UpdateStrategy,
   122  				fmt.Sprintf("must be '%s' or '%s'",
   123  					apps.RollingUpdateStatefulSetStrategyType,
   124  					apps.OnDeleteStatefulSetStrategyType)))
   125  	}
   126  
   127  	allErrs = append(allErrs, ValidatePersistentVolumeClaimRetentionPolicy(spec.PersistentVolumeClaimRetentionPolicy, fldPath.Child("persistentVolumeClaimRetentionPolicy"))...)
   128  
   129  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
   130  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   131  	if spec.Ordinals != nil {
   132  		replicaStartOrdinal := spec.Ordinals.Start
   133  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(replicaStartOrdinal), fldPath.Child("ordinals.start"))...)
   134  	}
   135  
   136  	if spec.Selector == nil {
   137  		allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
   138  	} else {
   139  		// validate selector strictly, spec.selector was always required to pass LabelSelectorAsSelector below
   140  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
   141  		if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   142  			allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for statefulset"))
   143  		}
   144  	}
   145  
   146  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   147  	if err != nil {
   148  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, ""))
   149  	} else {
   150  		allErrs = append(allErrs, ValidatePodTemplateSpecForStatefulSet(&spec.Template, selector, fldPath.Child("template"), opts)...)
   151  	}
   152  
   153  	if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
   154  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
   155  	}
   156  	if spec.Template.Spec.ActiveDeadlineSeconds != nil {
   157  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("template", "spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in StatefulSet is not Supported"))
   158  	}
   159  
   160  	return allErrs
   161  }
   162  
   163  // ValidateStatefulSet validates a StatefulSet.
   164  func ValidateStatefulSet(statefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   165  	allErrs := apivalidation.ValidateObjectMeta(&statefulSet.ObjectMeta, true, ValidateStatefulSetName, field.NewPath("metadata"))
   166  	allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
   167  	return allErrs
   168  }
   169  
   170  // ValidateStatefulSetUpdate tests if required fields in the StatefulSet are set.
   171  func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   172  	// First, validate that the new statefulset is valid.  Don't call
   173  	// ValidateStatefulSet() because we don't want to revalidate the name on
   174  	// update.  This is important here because we used to allow DNS subdomain
   175  	// for name, but that can't actually create pods.  The only reasonable
   176  	// thing to do it delete such an instance, but if there is a finalizer, it
   177  	// would need to pass update validation.  Name can't change anyway.
   178  	allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))
   179  	allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
   180  
   181  	// statefulset updates aren't super common and general updates are likely to be touching spec, so we'll do this
   182  	// deep copy right away.  This avoids mutating our inputs
   183  	newStatefulSetClone := statefulSet.DeepCopy()
   184  	newStatefulSetClone.Spec.Replicas = oldStatefulSet.Spec.Replicas               // +k8s:verify-mutation:reason=clone
   185  	newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template               // +k8s:verify-mutation:reason=clone
   186  	newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy   // +k8s:verify-mutation:reason=clone
   187  	newStatefulSetClone.Spec.MinReadySeconds = oldStatefulSet.Spec.MinReadySeconds // +k8s:verify-mutation:reason=clone
   188  	newStatefulSetClone.Spec.Ordinals = oldStatefulSet.Spec.Ordinals               // +k8s:verify-mutation:reason=clone
   189  
   190  	newStatefulSetClone.Spec.PersistentVolumeClaimRetentionPolicy = oldStatefulSet.Spec.PersistentVolumeClaimRetentionPolicy // +k8s:verify-mutation:reason=clone
   191  	if !apiequality.Semantic.DeepEqual(newStatefulSetClone.Spec, oldStatefulSet.Spec) {
   192  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden"))
   193  	}
   194  
   195  	return allErrs
   196  }
   197  
   198  // ValidateStatefulSetStatus validates a StatefulSetStatus.
   199  func ValidateStatefulSetStatus(status *apps.StatefulSetStatus, fieldPath *field.Path) field.ErrorList {
   200  	allErrs := field.ErrorList{}
   201  
   202  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fieldPath.Child("replicas"))...)
   203  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fieldPath.Child("readyReplicas"))...)
   204  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentReplicas), fieldPath.Child("currentReplicas"))...)
   205  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fieldPath.Child("updatedReplicas"))...)
   206  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fieldPath.Child("availableReplicas"))...)
   207  	if status.ObservedGeneration != nil {
   208  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.ObservedGeneration), fieldPath.Child("observedGeneration"))...)
   209  	}
   210  	if status.CollisionCount != nil {
   211  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fieldPath.Child("collisionCount"))...)
   212  	}
   213  
   214  	msg := "cannot be greater than status.replicas"
   215  	if status.ReadyReplicas > status.Replicas {
   216  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
   217  	}
   218  	if status.CurrentReplicas > status.Replicas {
   219  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("currentReplicas"), status.CurrentReplicas, msg))
   220  	}
   221  	if status.UpdatedReplicas > status.Replicas {
   222  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
   223  	}
   224  	if status.AvailableReplicas > status.Replicas {
   225  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
   226  	}
   227  	if status.AvailableReplicas > status.ReadyReplicas {
   228  		allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than status.readyReplicas"))
   229  	}
   230  	return allErrs
   231  }
   232  
   233  // ValidateStatefulSetStatusUpdate tests if required fields in the StatefulSet are set.
   234  func ValidateStatefulSetStatusUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
   235  	allErrs := field.ErrorList{}
   236  	allErrs = append(allErrs, ValidateStatefulSetStatus(&statefulSet.Status, field.NewPath("status"))...)
   237  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))...)
   238  	// TODO: Validate status.
   239  	if apivalidation.IsDecremented(statefulSet.Status.CollisionCount, oldStatefulSet.Status.CollisionCount) {
   240  		value := int32(0)
   241  		if statefulSet.Status.CollisionCount != nil {
   242  			value = *statefulSet.Status.CollisionCount
   243  		}
   244  		allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented"))
   245  	}
   246  	return allErrs
   247  }
   248  
   249  // ValidateControllerRevisionName can be used to check whether the given ControllerRevision name is valid.
   250  // Prefix indicates this name will be used as part of generation, in which case
   251  // trailing dashes are allowed.
   252  var ValidateControllerRevisionName = apimachineryvalidation.NameIsDNSSubdomain
   253  
   254  // ValidateControllerRevision collects errors for the fields of state and returns those errors as an ErrorList. If the
   255  // returned list is empty, state is valid. Validation is performed to ensure that state is a valid ObjectMeta, its name
   256  // is valid, and that it doesn't exceed the MaxControllerRevisionSize.
   257  func ValidateControllerRevision(revision *apps.ControllerRevision) field.ErrorList {
   258  	errs := field.ErrorList{}
   259  
   260  	errs = append(errs, apivalidation.ValidateObjectMeta(&revision.ObjectMeta, true, ValidateControllerRevisionName, field.NewPath("metadata"))...)
   261  	if revision.Data == nil {
   262  		errs = append(errs, field.Required(field.NewPath("data"), "data is mandatory"))
   263  	}
   264  	errs = append(errs, apivalidation.ValidateNonnegativeField(revision.Revision, field.NewPath("revision"))...)
   265  	return errs
   266  }
   267  
   268  // ValidateControllerRevisionUpdate collects errors pertaining to the mutation of an ControllerRevision Object. If the
   269  // returned ErrorList is empty the update operation is valid. Any mutation to the ControllerRevision's Data or Revision
   270  // is considered to be invalid.
   271  func ValidateControllerRevisionUpdate(newHistory, oldHistory *apps.ControllerRevision) field.ErrorList {
   272  	errs := field.ErrorList{}
   273  
   274  	errs = append(errs, apivalidation.ValidateObjectMetaUpdate(&newHistory.ObjectMeta, &oldHistory.ObjectMeta, field.NewPath("metadata"))...)
   275  	errs = append(errs, ValidateControllerRevision(newHistory)...)
   276  	errs = append(errs, apivalidation.ValidateImmutableField(newHistory.Data, oldHistory.Data, field.NewPath("data"))...)
   277  	return errs
   278  }
   279  
   280  // ValidateDaemonSet tests if required fields in the DaemonSet are set.
   281  func ValidateDaemonSet(ds *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   282  	allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata"))
   283  	allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, nil, field.NewPath("spec"), opts)...)
   284  	return allErrs
   285  }
   286  
   287  // ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
   288  func ValidateDaemonSetUpdate(ds, oldDS *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   289  	allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
   290  	allErrs = append(allErrs, ValidateDaemonSetSpecUpdate(&ds.Spec, &oldDS.Spec, field.NewPath("spec"))...)
   291  	allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, &oldDS.Spec, field.NewPath("spec"), opts)...)
   292  	return allErrs
   293  }
   294  
   295  // ValidateDaemonSetSpecUpdate tests if an update to a DaemonSetSpec is valid.
   296  func ValidateDaemonSetSpecUpdate(newSpec, oldSpec *apps.DaemonSetSpec, fldPath *field.Path) field.ErrorList {
   297  	allErrs := field.ErrorList{}
   298  
   299  	// TemplateGeneration shouldn't be decremented
   300  	if newSpec.TemplateGeneration < oldSpec.TemplateGeneration {
   301  		allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must not be decremented"))
   302  	}
   303  
   304  	// TemplateGeneration should be increased when and only when template is changed
   305  	templateUpdated := !apiequality.Semantic.DeepEqual(newSpec.Template, oldSpec.Template)
   306  	if newSpec.TemplateGeneration == oldSpec.TemplateGeneration && templateUpdated {
   307  		allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must be incremented upon template update"))
   308  	} else if newSpec.TemplateGeneration > oldSpec.TemplateGeneration && !templateUpdated {
   309  		allErrs = append(allErrs, field.Invalid(fldPath.Child("templateGeneration"), newSpec.TemplateGeneration, "must not be incremented without template update"))
   310  	}
   311  
   312  	return allErrs
   313  }
   314  
   315  // validateDaemonSetStatus validates a DaemonSetStatus
   316  func validateDaemonSetStatus(status *apps.DaemonSetStatus, fldPath *field.Path) field.ErrorList {
   317  	allErrs := field.ErrorList{}
   318  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentNumberScheduled), fldPath.Child("currentNumberScheduled"))...)
   319  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberMisscheduled), fldPath.Child("numberMisscheduled"))...)
   320  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredNumberScheduled), fldPath.Child("desiredNumberScheduled"))...)
   321  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberReady), fldPath.Child("numberReady"))...)
   322  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
   323  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedNumberScheduled), fldPath.Child("updatedNumberScheduled"))...)
   324  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...)
   325  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberUnavailable), fldPath.Child("numberUnavailable"))...)
   326  	if status.CollisionCount != nil {
   327  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
   328  	}
   329  	return allErrs
   330  }
   331  
   332  // ValidateDaemonSetStatusUpdate tests if required fields in the DaemonSet Status section
   333  func ValidateDaemonSetStatusUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
   334  	allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
   335  	allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...)
   336  	if apivalidation.IsDecremented(ds.Status.CollisionCount, oldDS.Status.CollisionCount) {
   337  		value := int32(0)
   338  		if ds.Status.CollisionCount != nil {
   339  			value = *ds.Status.CollisionCount
   340  		}
   341  		allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented"))
   342  	}
   343  	return allErrs
   344  }
   345  
   346  // ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
   347  func ValidateDaemonSetSpec(spec, oldSpec *apps.DaemonSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   348  	allErrs := field.ErrorList{}
   349  	labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector}
   350  
   351  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, labelSelectorValidationOpts, fldPath.Child("selector"))...)
   352  
   353  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   354  	if err == nil && !selector.Matches(labels.Set(spec.Template.Labels)) {
   355  		allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`"))
   356  	}
   357  	if spec.Selector != nil && len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   358  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for daemonset"))
   359  	}
   360  
   361  	allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"), opts)...)
   362  	// get rid of apivalidation.ValidateReadOnlyPersistentDisks,stop passing oldSpec to this function
   363  	var oldVols []api.Volume
   364  	if oldSpec != nil {
   365  		oldVols = oldSpec.Template.Spec.Volumes // +k8s:verify-mutation:reason=clone
   366  	}
   367  	allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, oldVols, fldPath.Child("template", "spec", "volumes"))...)
   368  	// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
   369  	if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
   370  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
   371  	}
   372  	if spec.Template.Spec.ActiveDeadlineSeconds != nil {
   373  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("template", "spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in DaemonSet is not Supported"))
   374  	}
   375  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   376  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...)
   377  
   378  	allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...)
   379  	if spec.RevisionHistoryLimit != nil {
   380  		// zero is a valid RevisionHistoryLimit
   381  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
   382  	}
   383  	return allErrs
   384  }
   385  
   386  // ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
   387  func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
   388  	var allErrs field.ErrorList
   389  
   390  	// Validate both fields are positive ints or have a percentage value
   391  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   392  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   393  
   394  	// Validate that MaxUnavailable and MaxSurge are not more than 100%.
   395  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   396  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   397  
   398  	// Validate exactly one of MaxSurge or MaxUnavailable is non-zero
   399  	hasUnavailable := getIntOrPercentValue(rollingUpdate.MaxUnavailable) != 0
   400  	hasSurge := getIntOrPercentValue(rollingUpdate.MaxSurge) != 0
   401  	switch {
   402  	case hasUnavailable && hasSurge:
   403  		allErrs = append(allErrs, field.Invalid(fldPath.Child("maxSurge"), rollingUpdate.MaxSurge, "may not be set when maxUnavailable is non-zero"))
   404  	case !hasUnavailable && !hasSurge:
   405  		allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
   406  	}
   407  
   408  	return allErrs
   409  }
   410  
   411  // validateRollingUpdateStatefulSet validates a given RollingUpdateStatefulSet.
   412  func validateRollingUpdateStatefulSet(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, fldPath *field.Path) field.ErrorList {
   413  	var allErrs field.ErrorList
   414  	fldPathMaxUn := fldPath.Child("maxUnavailable")
   415  	allErrs = append(allErrs,
   416  		apivalidation.ValidateNonnegativeField(
   417  			int64(rollingUpdate.Partition),
   418  			fldPath.Child("partition"))...)
   419  	if rollingUpdate.MaxUnavailable != nil {
   420  		allErrs = append(allErrs, ValidatePositiveIntOrPercent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
   421  		if getIntOrPercentValue(*rollingUpdate.MaxUnavailable) == 0 {
   422  			// MaxUnavailable cannot be 0.
   423  			allErrs = append(allErrs, field.Invalid(fldPathMaxUn, *rollingUpdate.MaxUnavailable, "cannot be 0"))
   424  		}
   425  		// Validate that MaxUnavailable is not more than 100%.
   426  		allErrs = append(allErrs, IsNotMoreThan100Percent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
   427  	}
   428  	return allErrs
   429  }
   430  
   431  // ValidateDaemonSetUpdateStrategy validates a given DaemonSetUpdateStrategy.
   432  func ValidateDaemonSetUpdateStrategy(strategy *apps.DaemonSetUpdateStrategy, fldPath *field.Path) field.ErrorList {
   433  	allErrs := field.ErrorList{}
   434  	switch strategy.Type {
   435  	case apps.OnDeleteDaemonSetStrategyType:
   436  	case apps.RollingUpdateDaemonSetStrategyType:
   437  		// Make sure RollingUpdate field isn't nil.
   438  		if strategy.RollingUpdate == nil {
   439  			allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), ""))
   440  			return allErrs
   441  		}
   442  		allErrs = append(allErrs, ValidateRollingUpdateDaemonSet(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
   443  	default:
   444  		validValues := []string{string(apps.RollingUpdateDaemonSetStrategyType), string(apps.OnDeleteDaemonSetStrategyType)}
   445  		allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
   446  	}
   447  	return allErrs
   448  }
   449  
   450  // ValidateDaemonSetName can be used to check whether the given daemon set name is valid.
   451  // Prefix indicates this name will be used as part of generation, in which case
   452  // trailing dashes are allowed.
   453  var ValidateDaemonSetName = apimachineryvalidation.NameIsDNSSubdomain
   454  
   455  // ValidateDeploymentName validates that the given name can be used as a deployment name.
   456  var ValidateDeploymentName = apimachineryvalidation.NameIsDNSSubdomain
   457  
   458  // ValidatePositiveIntOrPercent tests if a given value is a valid int or
   459  // percentage.
   460  func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
   461  	allErrs := field.ErrorList{}
   462  	switch intOrPercent.Type {
   463  	case intstr.String:
   464  		for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) {
   465  			allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg))
   466  		}
   467  	case intstr.Int:
   468  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
   469  	default:
   470  		allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%%')"))
   471  	}
   472  	return allErrs
   473  }
   474  
   475  func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
   476  	if intOrStringValue.Type != intstr.String {
   477  		return 0, false
   478  	}
   479  	if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 {
   480  		return 0, false
   481  	}
   482  	value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
   483  	return value, true
   484  }
   485  
   486  func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int {
   487  	value, isPercent := getPercentValue(intOrStringValue)
   488  	if isPercent {
   489  		return value
   490  	}
   491  	return intOrStringValue.IntValue()
   492  }
   493  
   494  // IsNotMoreThan100Percent tests is a value can be represented as a percentage
   495  // and if this value is not more than 100%.
   496  func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList {
   497  	allErrs := field.ErrorList{}
   498  	value, isPercent := getPercentValue(intOrStringValue)
   499  	if !isPercent || value <= 100 {
   500  		return nil
   501  	}
   502  	allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%"))
   503  	return allErrs
   504  }
   505  
   506  // ValidateRollingUpdateDeployment validates a given RollingUpdateDeployment.
   507  func ValidateRollingUpdateDeployment(rollingUpdate *apps.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList {
   508  	allErrs := field.ErrorList{}
   509  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   510  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   511  	if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 && getIntOrPercentValue(rollingUpdate.MaxSurge) == 0 {
   512  		// Both MaxSurge and MaxUnavailable cannot be zero.
   513  		allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "may not be 0 when `maxSurge` is 0"))
   514  	}
   515  	// Validate that MaxUnavailable is not more than 100%.
   516  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   517  	return allErrs
   518  }
   519  
   520  // ValidateDeploymentStrategy validates given DeploymentStrategy.
   521  func ValidateDeploymentStrategy(strategy *apps.DeploymentStrategy, fldPath *field.Path) field.ErrorList {
   522  	allErrs := field.ErrorList{}
   523  	switch strategy.Type {
   524  	case apps.RecreateDeploymentStrategyType:
   525  		if strategy.RollingUpdate != nil {
   526  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(apps.RecreateDeploymentStrategyType+"'")))
   527  		}
   528  	case apps.RollingUpdateDeploymentStrategyType:
   529  		// This should never happen since it's set and checked in defaults.go
   530  		if strategy.RollingUpdate == nil {
   531  			allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), "this should be defaulted and never be nil"))
   532  		} else {
   533  			allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
   534  		}
   535  	default:
   536  		validValues := []string{string(apps.RecreateDeploymentStrategyType), string(apps.RollingUpdateDeploymentStrategyType)}
   537  		allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
   538  	}
   539  	return allErrs
   540  }
   541  
   542  // ValidateRollback validates given RollbackConfig.
   543  func ValidateRollback(rollback *apps.RollbackConfig, fldPath *field.Path) field.ErrorList {
   544  	allErrs := field.ErrorList{}
   545  	v := rollback.Revision
   546  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(v), fldPath.Child("version"))...)
   547  	return allErrs
   548  }
   549  
   550  // ValidateDeploymentSpec validates given deployment spec.
   551  func ValidateDeploymentSpec(spec, oldSpec *apps.DeploymentSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   552  	allErrs := field.ErrorList{}
   553  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
   554  
   555  	if spec.Selector == nil {
   556  		allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
   557  	} else {
   558  		// validate selector strictly, spec.selector was always required to pass LabelSelectorAsSelector below
   559  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
   560  		if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   561  			allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
   562  		}
   563  	}
   564  
   565  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   566  	if err != nil {
   567  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
   568  	} else {
   569  		// oldSpec is not empty, pass oldSpec.template
   570  		var oldTemplate *api.PodTemplateSpec
   571  		if oldSpec != nil {
   572  			oldTemplate = &oldSpec.Template // +k8s:verify-mutation:reason=clone
   573  		}
   574  		allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, oldTemplate, selector, spec.Replicas, fldPath.Child("template"), opts)...)
   575  	}
   576  
   577  	allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
   578  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   579  	if spec.RevisionHistoryLimit != nil {
   580  		// zero is a valid RevisionHistoryLimit
   581  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
   582  	}
   583  	if spec.RollbackTo != nil {
   584  		allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...)
   585  	}
   586  	if spec.ProgressDeadlineSeconds != nil {
   587  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ProgressDeadlineSeconds), fldPath.Child("progressDeadlineSeconds"))...)
   588  		if *spec.ProgressDeadlineSeconds <= spec.MinReadySeconds {
   589  			allErrs = append(allErrs, field.Invalid(fldPath.Child("progressDeadlineSeconds"), spec.ProgressDeadlineSeconds, "must be greater than minReadySeconds"))
   590  		}
   591  	}
   592  	return allErrs
   593  }
   594  
   595  // ValidateDeploymentStatus validates given deployment status.
   596  func ValidateDeploymentStatus(status *apps.DeploymentStatus, fldPath *field.Path) field.ErrorList {
   597  	allErrs := field.ErrorList{}
   598  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
   599  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
   600  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fldPath.Child("updatedReplicas"))...)
   601  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
   602  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
   603  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
   604  	if status.CollisionCount != nil {
   605  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
   606  	}
   607  	msg := "cannot be greater than status.replicas"
   608  	if status.UpdatedReplicas > status.Replicas {
   609  		allErrs = append(allErrs, field.Invalid(fldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
   610  	}
   611  	if status.ReadyReplicas > status.Replicas {
   612  		allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
   613  	}
   614  	if status.AvailableReplicas > status.Replicas {
   615  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
   616  	}
   617  	if status.AvailableReplicas > status.ReadyReplicas {
   618  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
   619  	}
   620  	return allErrs
   621  }
   622  
   623  // ValidateDeploymentUpdate tests if an update to a Deployment is valid.
   624  func ValidateDeploymentUpdate(update, old *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
   625  	allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   626  	allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, &old.Spec, field.NewPath("spec"), opts)...)
   627  	return allErrs
   628  }
   629  
   630  // ValidateDeploymentStatusUpdate tests if a an update to a Deployment status
   631  // is valid.
   632  func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorList {
   633  	allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   634  	fldPath := field.NewPath("status")
   635  	allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, fldPath)...)
   636  	if apivalidation.IsDecremented(update.Status.CollisionCount, old.Status.CollisionCount) {
   637  		value := int32(0)
   638  		if update.Status.CollisionCount != nil {
   639  			value = *update.Status.CollisionCount
   640  		}
   641  		allErrs = append(allErrs, field.Invalid(fldPath.Child("collisionCount"), value, "cannot be decremented"))
   642  	}
   643  	return allErrs
   644  }
   645  
   646  // ValidateDeployment validates a given Deployment.
   647  func ValidateDeployment(obj *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
   648  	allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
   649  	allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, nil, field.NewPath("spec"), opts)...)
   650  	return allErrs
   651  }
   652  
   653  // ValidateDeploymentRollback validates a given DeploymentRollback.
   654  func ValidateDeploymentRollback(obj *apps.DeploymentRollback) field.ErrorList {
   655  	allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations"))
   656  	if len(obj.Name) == 0 {
   657  		allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required"))
   658  	}
   659  	allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...)
   660  	return allErrs
   661  }
   662  
   663  // ValidateReplicaSetName can be used to check whether the given ReplicaSet
   664  // name is valid.
   665  // Prefix indicates this name will be used as part of generation, in which case
   666  // trailing dashes are allowed.
   667  var ValidateReplicaSetName = apimachineryvalidation.NameIsDNSSubdomain
   668  
   669  // ValidateReplicaSet tests if required fields in the ReplicaSet are set.
   670  func ValidateReplicaSet(rs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   671  	allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
   672  	allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, nil, field.NewPath("spec"), opts)...)
   673  	return allErrs
   674  }
   675  
   676  // ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
   677  func ValidateReplicaSetUpdate(rs, oldRs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   678  	allErrs := field.ErrorList{}
   679  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
   680  	allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, &oldRs.Spec, field.NewPath("spec"), opts)...)
   681  	return allErrs
   682  }
   683  
   684  // ValidateReplicaSetStatusUpdate tests if required fields in the ReplicaSet are set.
   685  func ValidateReplicaSetStatusUpdate(rs, oldRs *apps.ReplicaSet) field.ErrorList {
   686  	allErrs := field.ErrorList{}
   687  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
   688  	allErrs = append(allErrs, ValidateReplicaSetStatus(rs.Status, field.NewPath("status"))...)
   689  	return allErrs
   690  }
   691  
   692  // ValidateReplicaSetStatus validates a given ReplicaSetStatus.
   693  func ValidateReplicaSetStatus(status apps.ReplicaSetStatus, fldPath *field.Path) field.ErrorList {
   694  	allErrs := field.ErrorList{}
   695  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
   696  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.FullyLabeledReplicas), fldPath.Child("fullyLabeledReplicas"))...)
   697  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
   698  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
   699  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ObservedGeneration), fldPath.Child("observedGeneration"))...)
   700  	msg := "cannot be greater than status.replicas"
   701  	if status.FullyLabeledReplicas > status.Replicas {
   702  		allErrs = append(allErrs, field.Invalid(fldPath.Child("fullyLabeledReplicas"), status.FullyLabeledReplicas, msg))
   703  	}
   704  	if status.ReadyReplicas > status.Replicas {
   705  		allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
   706  	}
   707  	if status.AvailableReplicas > status.Replicas {
   708  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
   709  	}
   710  	if status.AvailableReplicas > status.ReadyReplicas {
   711  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
   712  	}
   713  	return allErrs
   714  }
   715  
   716  // ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
   717  func ValidateReplicaSetSpec(spec, oldSpec *apps.ReplicaSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   718  	allErrs := field.ErrorList{}
   719  
   720  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
   721  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   722  
   723  	if spec.Selector == nil {
   724  		allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
   725  	} else {
   726  		// validate selector strictly, spec.selector was always required to pass LabelSelectorAsSelector below
   727  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
   728  		if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   729  			allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
   730  		}
   731  	}
   732  
   733  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   734  	if err != nil {
   735  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
   736  	} else {
   737  		// oldSpec is not empty, pass oldSpec.template.
   738  		var oldTemplate *api.PodTemplateSpec
   739  		if oldSpec != nil {
   740  			oldTemplate = &oldSpec.Template // +k8s:verify-mutation:reason=clone
   741  		}
   742  		allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, oldTemplate, selector, spec.Replicas, fldPath.Child("template"), opts)...)
   743  	}
   744  	return allErrs
   745  }
   746  
   747  // ValidatePodTemplateSpecForReplicaSet validates the given template and ensures that it is in accordance with the desired selector and replicas.
   748  func ValidatePodTemplateSpecForReplicaSet(template, oldTemplate *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   749  	allErrs := field.ErrorList{}
   750  	if template == nil {
   751  		allErrs = append(allErrs, field.Required(fldPath, ""))
   752  	} else {
   753  		if !selector.Empty() {
   754  			// Verify that the ReplicaSet selector matches the labels in template.
   755  			labels := labels.Set(template.Labels)
   756  			if !selector.Matches(labels) {
   757  				allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
   758  			}
   759  		}
   760  		allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath, opts)...)
   761  		// Daemons run on more than one node, Cancel verification of read and write volumes.
   762  		// get rid of apivalidation.ValidateReadOnlyPersistentDisks,stop passing oldTemplate to this function
   763  		var oldVols []api.Volume
   764  		if oldTemplate != nil {
   765  			oldVols = oldTemplate.Spec.Volumes // +k8s:verify-mutation:reason=clone
   766  		}
   767  		if replicas > 1 {
   768  			allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, oldVols, fldPath.Child("spec", "volumes"))...)
   769  		}
   770  		// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
   771  		if template.Spec.RestartPolicy != api.RestartPolicyAlways {
   772  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
   773  		}
   774  		if template.Spec.ActiveDeadlineSeconds != nil {
   775  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicaSet is not Supported"))
   776  		}
   777  	}
   778  	return allErrs
   779  }
   780  

View as plain text