github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/apis/meta/v1/validation/validation.go (about)

     1  /*
     2  Copyright 2015 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  	"regexp"
    22  	"unicode"
    23  
    24  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/types"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/sets"
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation/field"
    29  )
    30  
    31  // LabelSelectorValidationOptions is a struct that can be passed to ValidateLabelSelector to record the validate options
    32  type LabelSelectorValidationOptions struct {
    33  	// Allow invalid label value in selector
    34  	AllowInvalidLabelValueInSelector bool
    35  }
    36  
    37  // LabelSelectorHasInvalidLabelValue returns true if the given selector contains an invalid label value in a match expression.
    38  // This is useful for determining whether AllowInvalidLabelValueInSelector should be set to true when validating an update
    39  // based on existing persisted invalid values.
    40  func LabelSelectorHasInvalidLabelValue(ps *metav1.LabelSelector) bool {
    41  	if ps == nil {
    42  		return false
    43  	}
    44  	for _, e := range ps.MatchExpressions {
    45  		for _, v := range e.Values {
    46  			if len(validation.IsValidLabelValue(v)) > 0 {
    47  				return true
    48  			}
    49  		}
    50  	}
    51  	return false
    52  }
    53  
    54  // ValidateLabelSelector validate the LabelSelector according to the opts and returns any validation errors.
    55  // opts.AllowInvalidLabelValueInSelector is only expected to be set to true when required for backwards compatibility with existing invalid data.
    56  func ValidateLabelSelector(ps *metav1.LabelSelector, opts LabelSelectorValidationOptions, fldPath *field.Path) field.ErrorList {
    57  	allErrs := field.ErrorList{}
    58  	if ps == nil {
    59  		return allErrs
    60  	}
    61  	allErrs = append(allErrs, ValidateLabels(ps.MatchLabels, fldPath.Child("matchLabels"))...)
    62  	for i, expr := range ps.MatchExpressions {
    63  		allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, opts, fldPath.Child("matchExpressions").Index(i))...)
    64  	}
    65  	return allErrs
    66  }
    67  
    68  // ValidateLabelSelectorRequirement validate the requirement according to the opts and returns any validation errors.
    69  // opts.AllowInvalidLabelValueInSelector is only expected to be set to true when required for backwards compatibility with existing invalid data.
    70  func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, opts LabelSelectorValidationOptions, fldPath *field.Path) field.ErrorList {
    71  	allErrs := field.ErrorList{}
    72  	switch sr.Operator {
    73  	case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
    74  		if len(sr.Values) == 0 {
    75  			allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
    76  		}
    77  	case metav1.LabelSelectorOpExists, metav1.LabelSelectorOpDoesNotExist:
    78  		if len(sr.Values) > 0 {
    79  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
    80  		}
    81  	default:
    82  		allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
    83  	}
    84  	allErrs = append(allErrs, ValidateLabelName(sr.Key, fldPath.Child("key"))...)
    85  	if !opts.AllowInvalidLabelValueInSelector {
    86  		for valueIndex, value := range sr.Values {
    87  			for _, msg := range validation.IsValidLabelValue(value) {
    88  				allErrs = append(allErrs, field.Invalid(fldPath.Child("values").Index(valueIndex), value, msg))
    89  			}
    90  		}
    91  	}
    92  	return allErrs
    93  }
    94  
    95  // ValidateLabelName validates that the label name is correctly defined.
    96  func ValidateLabelName(labelName string, fldPath *field.Path) field.ErrorList {
    97  	allErrs := field.ErrorList{}
    98  	for _, msg := range validation.IsQualifiedName(labelName) {
    99  		allErrs = append(allErrs, field.Invalid(fldPath, labelName, msg))
   100  	}
   101  	return allErrs
   102  }
   103  
   104  // ValidateLabels validates that a set of labels are correctly defined.
   105  func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorList {
   106  	allErrs := field.ErrorList{}
   107  	for k, v := range labels {
   108  		allErrs = append(allErrs, ValidateLabelName(k, fldPath)...)
   109  		for _, msg := range validation.IsValidLabelValue(v) {
   110  			allErrs = append(allErrs, field.Invalid(fldPath, v, msg))
   111  		}
   112  	}
   113  	return allErrs
   114  }
   115  
   116  func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList {
   117  	allErrs := field.ErrorList{}
   118  	//lint:file-ignore SA1019 Keep validation for deprecated OrphanDependents option until it's being removed
   119  	if options.OrphanDependents != nil && options.PropagationPolicy != nil {
   120  		allErrs = append(allErrs, field.Invalid(field.NewPath("propagationPolicy"), options.PropagationPolicy, "orphanDependents and deletionPropagation cannot be both set"))
   121  	}
   122  	if options.PropagationPolicy != nil &&
   123  		*options.PropagationPolicy != metav1.DeletePropagationForeground &&
   124  		*options.PropagationPolicy != metav1.DeletePropagationBackground &&
   125  		*options.PropagationPolicy != metav1.DeletePropagationOrphan {
   126  		allErrs = append(allErrs, field.NotSupported(field.NewPath("propagationPolicy"), options.PropagationPolicy, []string{string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground), string(metav1.DeletePropagationOrphan), "nil"}))
   127  	}
   128  	allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
   129  	return allErrs
   130  }
   131  
   132  func ValidateCreateOptions(options *metav1.CreateOptions) field.ErrorList {
   133  	allErrs := field.ErrorList{}
   134  	allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
   135  	allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
   136  	allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
   137  	return allErrs
   138  }
   139  
   140  func ValidateUpdateOptions(options *metav1.UpdateOptions) field.ErrorList {
   141  	allErrs := field.ErrorList{}
   142  	allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
   143  	allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
   144  	allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
   145  	return allErrs
   146  }
   147  
   148  func ValidatePatchOptions(options *metav1.PatchOptions, patchType types.PatchType) field.ErrorList {
   149  	allErrs := field.ErrorList{}
   150  	if patchType != types.ApplyPatchType {
   151  		if options.Force != nil {
   152  			allErrs = append(allErrs, field.Forbidden(field.NewPath("force"), "may not be specified for non-apply patch"))
   153  		}
   154  	} else {
   155  		if options.FieldManager == "" {
   156  			// This field is defaulted to "kubectl" by kubectl, but HAS TO be explicitly set by controllers.
   157  			allErrs = append(allErrs, field.Required(field.NewPath("fieldManager"), "is required for apply patch"))
   158  		}
   159  	}
   160  	allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
   161  	allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
   162  	allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
   163  	return allErrs
   164  }
   165  
   166  var FieldManagerMaxLength = 128
   167  
   168  // ValidateFieldManager valides that the fieldManager is the proper length and
   169  // only has printable characters.
   170  func ValidateFieldManager(fieldManager string, fldPath *field.Path) field.ErrorList {
   171  	allErrs := field.ErrorList{}
   172  	// the field can not be set as a `*string`, so a empty string ("") is
   173  	// considered as not set and is defaulted by the rest of the process
   174  	// (unless apply is used, in which case it is required).
   175  	if len(fieldManager) > FieldManagerMaxLength {
   176  		allErrs = append(allErrs, field.TooLong(fldPath, fieldManager, FieldManagerMaxLength))
   177  	}
   178  	// Verify that all characters are printable.
   179  	for i, r := range fieldManager {
   180  		if !unicode.IsPrint(r) {
   181  			allErrs = append(allErrs, field.Invalid(fldPath, fieldManager, fmt.Sprintf("invalid character %#U (at position %d)", r, i)))
   182  		}
   183  	}
   184  
   185  	return allErrs
   186  }
   187  
   188  var allowedDryRunValues = sets.NewString(metav1.DryRunAll)
   189  
   190  // ValidateDryRun validates that a dryRun query param only contains allowed values.
   191  func ValidateDryRun(fldPath *field.Path, dryRun []string) field.ErrorList {
   192  	allErrs := field.ErrorList{}
   193  	if !allowedDryRunValues.HasAll(dryRun...) {
   194  		allErrs = append(allErrs, field.NotSupported(fldPath, dryRun, allowedDryRunValues.List()))
   195  	}
   196  	return allErrs
   197  }
   198  
   199  var allowedFieldValidationValues = sets.NewString("", metav1.FieldValidationIgnore, metav1.FieldValidationWarn, metav1.FieldValidationStrict)
   200  
   201  // ValidateFieldValidation validates that a fieldValidation query param only contains allowed values.
   202  func ValidateFieldValidation(fldPath *field.Path, fieldValidation string) field.ErrorList {
   203  	allErrs := field.ErrorList{}
   204  	if !allowedFieldValidationValues.Has(fieldValidation) {
   205  		allErrs = append(allErrs, field.NotSupported(fldPath, fieldValidation, allowedFieldValidationValues.List()))
   206  	}
   207  	return allErrs
   208  
   209  }
   210  
   211  const UninitializedStatusUpdateErrorMsg string = `must not update status when the object is uninitialized`
   212  
   213  // ValidateTableOptions returns any invalid flags on TableOptions.
   214  func ValidateTableOptions(opts *metav1.TableOptions) field.ErrorList {
   215  	var allErrs field.ErrorList
   216  	switch opts.IncludeObject {
   217  	case metav1.IncludeMetadata, metav1.IncludeNone, metav1.IncludeObject, "":
   218  	default:
   219  		allErrs = append(allErrs, field.Invalid(field.NewPath("includeObject"), opts.IncludeObject, "must be 'Metadata', 'Object', 'None', or empty"))
   220  	}
   221  	return allErrs
   222  }
   223  
   224  const MaxSubresourceNameLength = 256
   225  
   226  func ValidateManagedFields(fieldsList []metav1.ManagedFieldsEntry, fldPath *field.Path) field.ErrorList {
   227  	var allErrs field.ErrorList
   228  	for i, fields := range fieldsList {
   229  		fldPath := fldPath.Index(i)
   230  		switch fields.Operation {
   231  		case metav1.ManagedFieldsOperationApply, metav1.ManagedFieldsOperationUpdate:
   232  		default:
   233  			allErrs = append(allErrs, field.Invalid(fldPath.Child("operation"), fields.Operation, "must be `Apply` or `Update`"))
   234  		}
   235  		if len(fields.FieldsType) > 0 && fields.FieldsType != "FieldsV1" {
   236  			allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldsType"), fields.FieldsType, "must be `FieldsV1`"))
   237  		}
   238  		allErrs = append(allErrs, ValidateFieldManager(fields.Manager, fldPath.Child("manager"))...)
   239  
   240  		if len(fields.Subresource) > MaxSubresourceNameLength {
   241  			allErrs = append(allErrs, field.TooLong(fldPath.Child("subresource"), fields.Subresource, MaxSubresourceNameLength))
   242  		}
   243  	}
   244  	return allErrs
   245  }
   246  
   247  func ValidateConditions(conditions []metav1.Condition, fldPath *field.Path) field.ErrorList {
   248  	var allErrs field.ErrorList
   249  
   250  	conditionTypeToFirstIndex := map[string]int{}
   251  	for i, condition := range conditions {
   252  		if _, ok := conditionTypeToFirstIndex[condition.Type]; ok {
   253  			allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("type"), condition.Type))
   254  		} else {
   255  			conditionTypeToFirstIndex[condition.Type] = i
   256  		}
   257  
   258  		allErrs = append(allErrs, ValidateCondition(condition, fldPath.Index(i))...)
   259  	}
   260  
   261  	return allErrs
   262  }
   263  
   264  // validConditionStatuses is used internally to check validity and provide a good message
   265  var validConditionStatuses = sets.NewString(string(metav1.ConditionTrue), string(metav1.ConditionFalse), string(metav1.ConditionUnknown))
   266  
   267  const (
   268  	maxReasonLen  = 1 * 1024
   269  	maxMessageLen = 32 * 1024
   270  )
   271  
   272  func ValidateCondition(condition metav1.Condition, fldPath *field.Path) field.ErrorList {
   273  	var allErrs field.ErrorList
   274  
   275  	// type is set and is a valid format
   276  	allErrs = append(allErrs, ValidateLabelName(condition.Type, fldPath.Child("type"))...)
   277  
   278  	// status is set and is an accepted value
   279  	if !validConditionStatuses.Has(string(condition.Status)) {
   280  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("status"), condition.Status, validConditionStatuses.List()))
   281  	}
   282  
   283  	if condition.ObservedGeneration < 0 {
   284  		allErrs = append(allErrs, field.Invalid(fldPath.Child("observedGeneration"), condition.ObservedGeneration, "must be greater than or equal to zero"))
   285  	}
   286  
   287  	if condition.LastTransitionTime.IsZero() {
   288  		allErrs = append(allErrs, field.Required(fldPath.Child("lastTransitionTime"), "must be set"))
   289  	}
   290  
   291  	if len(condition.Reason) == 0 {
   292  		allErrs = append(allErrs, field.Required(fldPath.Child("reason"), "must be set"))
   293  	} else {
   294  		for _, currErr := range isValidConditionReason(condition.Reason) {
   295  			allErrs = append(allErrs, field.Invalid(fldPath.Child("reason"), condition.Reason, currErr))
   296  		}
   297  		if len(condition.Reason) > maxReasonLen {
   298  			allErrs = append(allErrs, field.TooLong(fldPath.Child("reason"), condition.Reason, maxReasonLen))
   299  		}
   300  	}
   301  
   302  	if len(condition.Message) > maxMessageLen {
   303  		allErrs = append(allErrs, field.TooLong(fldPath.Child("message"), condition.Message, maxMessageLen))
   304  	}
   305  
   306  	return allErrs
   307  }
   308  
   309  const conditionReasonFmt string = "[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?"
   310  const conditionReasonErrMsg string = "a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_'"
   311  
   312  var conditionReasonRegexp = regexp.MustCompile("^" + conditionReasonFmt + "$")
   313  
   314  // isValidConditionReason tests for a string that conforms to rules for condition reasons. This checks the format, but not the length.
   315  func isValidConditionReason(value string) []string {
   316  	if !conditionReasonRegexp.MatchString(value) {
   317  		return []string{validation.RegexError(conditionReasonErrMsg, conditionReasonFmt, "my_name", "MY_NAME", "MyName", "ReasonA,ReasonB", "ReasonA:ReasonB")}
   318  	}
   319  	return nil
   320  }