k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/apis/apps/validation/validation.go (about)

     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, 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, 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 *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  	// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
   363  	if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
   364  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
   365  	}
   366  	if spec.Template.Spec.ActiveDeadlineSeconds != nil {
   367  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("template", "spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in DaemonSet is not Supported"))
   368  	}
   369  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   370  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...)
   371  
   372  	allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...)
   373  	if spec.RevisionHistoryLimit != nil {
   374  		// zero is a valid RevisionHistoryLimit
   375  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
   376  	}
   377  	return allErrs
   378  }
   379  
   380  // ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
   381  func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
   382  	var allErrs field.ErrorList
   383  
   384  	// Validate both fields are positive ints or have a percentage value
   385  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   386  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   387  
   388  	// Validate that MaxUnavailable and MaxSurge are not more than 100%.
   389  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   390  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   391  
   392  	// Validate exactly one of MaxSurge or MaxUnavailable is non-zero
   393  	hasUnavailable := getIntOrPercentValue(rollingUpdate.MaxUnavailable) != 0
   394  	hasSurge := getIntOrPercentValue(rollingUpdate.MaxSurge) != 0
   395  	switch {
   396  	case hasUnavailable && hasSurge:
   397  		allErrs = append(allErrs, field.Invalid(fldPath.Child("maxSurge"), rollingUpdate.MaxSurge, "may not be set when maxUnavailable is non-zero"))
   398  	case !hasUnavailable && !hasSurge:
   399  		allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
   400  	}
   401  
   402  	return allErrs
   403  }
   404  
   405  // validateRollingUpdateStatefulSet validates a given RollingUpdateStatefulSet.
   406  func validateRollingUpdateStatefulSet(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, fldPath *field.Path) field.ErrorList {
   407  	var allErrs field.ErrorList
   408  	fldPathMaxUn := fldPath.Child("maxUnavailable")
   409  	allErrs = append(allErrs,
   410  		apivalidation.ValidateNonnegativeField(
   411  			int64(rollingUpdate.Partition),
   412  			fldPath.Child("partition"))...)
   413  	if rollingUpdate.MaxUnavailable != nil {
   414  		allErrs = append(allErrs, ValidatePositiveIntOrPercent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
   415  		if getIntOrPercentValue(*rollingUpdate.MaxUnavailable) == 0 {
   416  			// MaxUnavailable cannot be 0.
   417  			allErrs = append(allErrs, field.Invalid(fldPathMaxUn, *rollingUpdate.MaxUnavailable, "cannot be 0"))
   418  		}
   419  		// Validate that MaxUnavailable is not more than 100%.
   420  		allErrs = append(allErrs, IsNotMoreThan100Percent(*rollingUpdate.MaxUnavailable, fldPathMaxUn)...)
   421  	}
   422  	return allErrs
   423  }
   424  
   425  // ValidateDaemonSetUpdateStrategy validates a given DaemonSetUpdateStrategy.
   426  func ValidateDaemonSetUpdateStrategy(strategy *apps.DaemonSetUpdateStrategy, fldPath *field.Path) field.ErrorList {
   427  	allErrs := field.ErrorList{}
   428  	switch strategy.Type {
   429  	case apps.OnDeleteDaemonSetStrategyType:
   430  	case apps.RollingUpdateDaemonSetStrategyType:
   431  		// Make sure RollingUpdate field isn't nil.
   432  		if strategy.RollingUpdate == nil {
   433  			allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), ""))
   434  			return allErrs
   435  		}
   436  		allErrs = append(allErrs, ValidateRollingUpdateDaemonSet(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
   437  	default:
   438  		validValues := []string{string(apps.RollingUpdateDaemonSetStrategyType), string(apps.OnDeleteDaemonSetStrategyType)}
   439  		allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
   440  	}
   441  	return allErrs
   442  }
   443  
   444  // ValidateDaemonSetName can be used to check whether the given daemon set name is valid.
   445  // Prefix indicates this name will be used as part of generation, in which case
   446  // trailing dashes are allowed.
   447  var ValidateDaemonSetName = apimachineryvalidation.NameIsDNSSubdomain
   448  
   449  // ValidateDeploymentName validates that the given name can be used as a deployment name.
   450  var ValidateDeploymentName = apimachineryvalidation.NameIsDNSSubdomain
   451  
   452  // ValidatePositiveIntOrPercent tests if a given value is a valid int or
   453  // percentage.
   454  func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
   455  	allErrs := field.ErrorList{}
   456  	switch intOrPercent.Type {
   457  	case intstr.String:
   458  		for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) {
   459  			allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg))
   460  		}
   461  	case intstr.Int:
   462  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
   463  	default:
   464  		allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%%')"))
   465  	}
   466  	return allErrs
   467  }
   468  
   469  func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
   470  	if intOrStringValue.Type != intstr.String {
   471  		return 0, false
   472  	}
   473  	if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 {
   474  		return 0, false
   475  	}
   476  	value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
   477  	return value, true
   478  }
   479  
   480  func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int {
   481  	value, isPercent := getPercentValue(intOrStringValue)
   482  	if isPercent {
   483  		return value
   484  	}
   485  	return intOrStringValue.IntValue()
   486  }
   487  
   488  // IsNotMoreThan100Percent tests is a value can be represented as a percentage
   489  // and if this value is not more than 100%.
   490  func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList {
   491  	allErrs := field.ErrorList{}
   492  	value, isPercent := getPercentValue(intOrStringValue)
   493  	if !isPercent || value <= 100 {
   494  		return nil
   495  	}
   496  	allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%"))
   497  	return allErrs
   498  }
   499  
   500  // ValidateRollingUpdateDeployment validates a given RollingUpdateDeployment.
   501  func ValidateRollingUpdateDeployment(rollingUpdate *apps.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList {
   502  	allErrs := field.ErrorList{}
   503  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   504  	allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
   505  	if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 && getIntOrPercentValue(rollingUpdate.MaxSurge) == 0 {
   506  		// Both MaxSurge and MaxUnavailable cannot be zero.
   507  		allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "may not be 0 when `maxSurge` is 0"))
   508  	}
   509  	// Validate that MaxUnavailable is not more than 100%.
   510  	allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
   511  	return allErrs
   512  }
   513  
   514  // ValidateDeploymentStrategy validates given DeploymentStrategy.
   515  func ValidateDeploymentStrategy(strategy *apps.DeploymentStrategy, fldPath *field.Path) field.ErrorList {
   516  	allErrs := field.ErrorList{}
   517  	switch strategy.Type {
   518  	case apps.RecreateDeploymentStrategyType:
   519  		if strategy.RollingUpdate != nil {
   520  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(apps.RecreateDeploymentStrategyType+"'")))
   521  		}
   522  	case apps.RollingUpdateDeploymentStrategyType:
   523  		// This should never happen since it's set and checked in defaults.go
   524  		if strategy.RollingUpdate == nil {
   525  			allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), "this should be defaulted and never be nil"))
   526  		} else {
   527  			allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
   528  		}
   529  	default:
   530  		validValues := []string{string(apps.RecreateDeploymentStrategyType), string(apps.RollingUpdateDeploymentStrategyType)}
   531  		allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
   532  	}
   533  	return allErrs
   534  }
   535  
   536  // ValidateRollback validates given RollbackConfig.
   537  func ValidateRollback(rollback *apps.RollbackConfig, fldPath *field.Path) field.ErrorList {
   538  	allErrs := field.ErrorList{}
   539  	v := rollback.Revision
   540  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(v), fldPath.Child("version"))...)
   541  	return allErrs
   542  }
   543  
   544  // ValidateDeploymentSpec validates given deployment spec.
   545  func ValidateDeploymentSpec(spec, oldSpec *apps.DeploymentSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   546  	allErrs := field.ErrorList{}
   547  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
   548  
   549  	if spec.Selector == nil {
   550  		allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
   551  	} else {
   552  		// validate selector strictly, spec.selector was always required to pass LabelSelectorAsSelector below
   553  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
   554  		if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   555  			allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
   556  		}
   557  	}
   558  
   559  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   560  	if err != nil {
   561  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
   562  	} else {
   563  		allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"), opts)...)
   564  	}
   565  
   566  	allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
   567  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   568  	if spec.RevisionHistoryLimit != nil {
   569  		// zero is a valid RevisionHistoryLimit
   570  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
   571  	}
   572  	if spec.RollbackTo != nil {
   573  		allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...)
   574  	}
   575  	if spec.ProgressDeadlineSeconds != nil {
   576  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ProgressDeadlineSeconds), fldPath.Child("progressDeadlineSeconds"))...)
   577  		if *spec.ProgressDeadlineSeconds <= spec.MinReadySeconds {
   578  			allErrs = append(allErrs, field.Invalid(fldPath.Child("progressDeadlineSeconds"), spec.ProgressDeadlineSeconds, "must be greater than minReadySeconds"))
   579  		}
   580  	}
   581  	return allErrs
   582  }
   583  
   584  // ValidateDeploymentStatus validates given deployment status.
   585  func ValidateDeploymentStatus(status *apps.DeploymentStatus, fldPath *field.Path) field.ErrorList {
   586  	allErrs := field.ErrorList{}
   587  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
   588  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
   589  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fldPath.Child("updatedReplicas"))...)
   590  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
   591  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
   592  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
   593  	if status.CollisionCount != nil {
   594  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
   595  	}
   596  	msg := "cannot be greater than status.replicas"
   597  	if status.UpdatedReplicas > status.Replicas {
   598  		allErrs = append(allErrs, field.Invalid(fldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
   599  	}
   600  	if status.ReadyReplicas > status.Replicas {
   601  		allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
   602  	}
   603  	if status.AvailableReplicas > status.Replicas {
   604  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
   605  	}
   606  	if status.AvailableReplicas > status.ReadyReplicas {
   607  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
   608  	}
   609  	return allErrs
   610  }
   611  
   612  // ValidateDeploymentUpdate tests if an update to a Deployment is valid.
   613  func ValidateDeploymentUpdate(update, old *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
   614  	allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   615  	allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, &old.Spec, field.NewPath("spec"), opts)...)
   616  	return allErrs
   617  }
   618  
   619  // ValidateDeploymentStatusUpdate tests if a an update to a Deployment status
   620  // is valid.
   621  func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorList {
   622  	allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   623  	fldPath := field.NewPath("status")
   624  	allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, fldPath)...)
   625  	if apivalidation.IsDecremented(update.Status.CollisionCount, old.Status.CollisionCount) {
   626  		value := int32(0)
   627  		if update.Status.CollisionCount != nil {
   628  			value = *update.Status.CollisionCount
   629  		}
   630  		allErrs = append(allErrs, field.Invalid(fldPath.Child("collisionCount"), value, "cannot be decremented"))
   631  	}
   632  	return allErrs
   633  }
   634  
   635  // ValidateDeployment validates a given Deployment.
   636  func ValidateDeployment(obj *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
   637  	allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
   638  	allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, nil, field.NewPath("spec"), opts)...)
   639  	return allErrs
   640  }
   641  
   642  // ValidateDeploymentRollback validates a given DeploymentRollback.
   643  func ValidateDeploymentRollback(obj *apps.DeploymentRollback) field.ErrorList {
   644  	allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations"))
   645  	if len(obj.Name) == 0 {
   646  		allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required"))
   647  	}
   648  	allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...)
   649  	return allErrs
   650  }
   651  
   652  // ValidateReplicaSetName can be used to check whether the given ReplicaSet
   653  // name is valid.
   654  // Prefix indicates this name will be used as part of generation, in which case
   655  // trailing dashes are allowed.
   656  var ValidateReplicaSetName = apimachineryvalidation.NameIsDNSSubdomain
   657  
   658  // ValidateReplicaSet tests if required fields in the ReplicaSet are set.
   659  func ValidateReplicaSet(rs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   660  	allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
   661  	allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, nil, field.NewPath("spec"), opts)...)
   662  	return allErrs
   663  }
   664  
   665  // ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
   666  func ValidateReplicaSetUpdate(rs, oldRs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
   667  	allErrs := field.ErrorList{}
   668  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
   669  	allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, &oldRs.Spec, field.NewPath("spec"), opts)...)
   670  	return allErrs
   671  }
   672  
   673  // ValidateReplicaSetStatusUpdate tests if required fields in the ReplicaSet are set.
   674  func ValidateReplicaSetStatusUpdate(rs, oldRs *apps.ReplicaSet) field.ErrorList {
   675  	allErrs := field.ErrorList{}
   676  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
   677  	allErrs = append(allErrs, ValidateReplicaSetStatus(rs.Status, field.NewPath("status"))...)
   678  	return allErrs
   679  }
   680  
   681  // ValidateReplicaSetStatus validates a given ReplicaSetStatus.
   682  func ValidateReplicaSetStatus(status apps.ReplicaSetStatus, fldPath *field.Path) field.ErrorList {
   683  	allErrs := field.ErrorList{}
   684  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
   685  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.FullyLabeledReplicas), fldPath.Child("fullyLabeledReplicas"))...)
   686  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
   687  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
   688  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ObservedGeneration), fldPath.Child("observedGeneration"))...)
   689  	msg := "cannot be greater than status.replicas"
   690  	if status.FullyLabeledReplicas > status.Replicas {
   691  		allErrs = append(allErrs, field.Invalid(fldPath.Child("fullyLabeledReplicas"), status.FullyLabeledReplicas, msg))
   692  	}
   693  	if status.ReadyReplicas > status.Replicas {
   694  		allErrs = append(allErrs, field.Invalid(fldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
   695  	}
   696  	if status.AvailableReplicas > status.Replicas {
   697  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
   698  	}
   699  	if status.AvailableReplicas > status.ReadyReplicas {
   700  		allErrs = append(allErrs, field.Invalid(fldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
   701  	}
   702  	return allErrs
   703  }
   704  
   705  // ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
   706  func ValidateReplicaSetSpec(spec, oldSpec *apps.ReplicaSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   707  	allErrs := field.ErrorList{}
   708  
   709  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
   710  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
   711  
   712  	if spec.Selector == nil {
   713  		allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
   714  	} else {
   715  		// validate selector strictly, spec.selector was always required to pass LabelSelectorAsSelector below
   716  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, fldPath.Child("selector"))...)
   717  		if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
   718  			allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for deployment"))
   719  		}
   720  	}
   721  
   722  	selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
   723  	if err != nil {
   724  		allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
   725  	} else {
   726  		allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"), opts)...)
   727  	}
   728  	return allErrs
   729  }
   730  
   731  // ValidatePodTemplateSpecForReplicaSet validates the given template and ensures that it is in accordance with the desired selector and replicas.
   732  func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
   733  	allErrs := field.ErrorList{}
   734  	if template == nil {
   735  		allErrs = append(allErrs, field.Required(fldPath, ""))
   736  	} else {
   737  		if !selector.Empty() {
   738  			// Verify that the ReplicaSet selector matches the labels in template.
   739  			labels := labels.Set(template.Labels)
   740  			if !selector.Matches(labels) {
   741  				allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
   742  			}
   743  		}
   744  		allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath, opts)...)
   745  		// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
   746  		if template.Spec.RestartPolicy != api.RestartPolicyAlways {
   747  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
   748  		}
   749  		if template.Spec.ActiveDeadlineSeconds != nil {
   750  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicaSet is not Supported"))
   751  		}
   752  	}
   753  	return allErrs
   754  }