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