github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/validation/field/errors.go (about)

     1  /*
     2  Copyright 2014 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 field
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  
    25  	utilerrors "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/errors"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/sets"
    27  )
    28  
    29  // Error is an implementation of the 'error' interface, which represents a
    30  // field-level validation error.
    31  type Error struct {
    32  	Type     ErrorType
    33  	Field    string
    34  	BadValue interface{}
    35  	Detail   string
    36  }
    37  
    38  var _ error = &Error{}
    39  
    40  // Error implements the error interface.
    41  func (v *Error) Error() string {
    42  	return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
    43  }
    44  
    45  type OmitValueType struct{}
    46  
    47  var omitValue = OmitValueType{}
    48  
    49  // ErrorBody returns the error message without the field name.  This is useful
    50  // for building nice-looking higher-level error reporting.
    51  func (v *Error) ErrorBody() string {
    52  	var s string
    53  	switch {
    54  	case v.Type == ErrorTypeRequired:
    55  		s = v.Type.String()
    56  	case v.Type == ErrorTypeForbidden:
    57  		s = v.Type.String()
    58  	case v.Type == ErrorTypeTooLong:
    59  		s = v.Type.String()
    60  	case v.Type == ErrorTypeInternal:
    61  		s = v.Type.String()
    62  	case v.BadValue == omitValue:
    63  		s = v.Type.String()
    64  	default:
    65  		value := v.BadValue
    66  		valueType := reflect.TypeOf(value)
    67  		if value == nil || valueType == nil {
    68  			value = "null"
    69  		} else if valueType.Kind() == reflect.Pointer {
    70  			if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
    71  				value = "null"
    72  			} else {
    73  				value = reflectValue.Elem().Interface()
    74  			}
    75  		}
    76  		switch t := value.(type) {
    77  		case int64, int32, float64, float32, bool:
    78  			// use simple printer for simple types
    79  			s = fmt.Sprintf("%s: %v", v.Type, value)
    80  		case string:
    81  			s = fmt.Sprintf("%s: %q", v.Type, t)
    82  		case fmt.Stringer:
    83  			// anything that defines String() is better than raw struct
    84  			s = fmt.Sprintf("%s: %s", v.Type, t.String())
    85  		default:
    86  			// fallback to raw struct
    87  			// TODO: internal types have panic guards against json.Marshalling to prevent
    88  			// accidental use of internal types in external serialized form.  For now, use
    89  			// %#v, although it would be better to show a more expressive output in the future
    90  			s = fmt.Sprintf("%s: %#v", v.Type, value)
    91  		}
    92  	}
    93  	if len(v.Detail) != 0 {
    94  		s += fmt.Sprintf(": %s", v.Detail)
    95  	}
    96  	return s
    97  }
    98  
    99  // ErrorType is a machine readable value providing more detail about why
   100  // a field is invalid.  These values are expected to match 1-1 with
   101  // CauseType in api/types.go.
   102  type ErrorType string
   103  
   104  // TODO: These values are duplicated in api/types.go, but there's a circular dep.  Fix it.
   105  const (
   106  	// ErrorTypeNotFound is used to report failure to find a requested value
   107  	// (e.g. looking up an ID).  See NotFound().
   108  	ErrorTypeNotFound ErrorType = "FieldValueNotFound"
   109  	// ErrorTypeRequired is used to report required values that are not
   110  	// provided (e.g. empty strings, null values, or empty arrays).  See
   111  	// Required().
   112  	ErrorTypeRequired ErrorType = "FieldValueRequired"
   113  	// ErrorTypeDuplicate is used to report collisions of values that must be
   114  	// unique (e.g. unique IDs).  See Duplicate().
   115  	ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
   116  	// ErrorTypeInvalid is used to report malformed values (e.g. failed regex
   117  	// match, too long, out of bounds).  See Invalid().
   118  	ErrorTypeInvalid ErrorType = "FieldValueInvalid"
   119  	// ErrorTypeNotSupported is used to report unknown values for enumerated
   120  	// fields (e.g. a list of valid values).  See NotSupported().
   121  	ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
   122  	// ErrorTypeForbidden is used to report valid (as per formatting rules)
   123  	// values which would be accepted under some conditions, but which are not
   124  	// permitted by the current conditions (such as security policy).  See
   125  	// Forbidden().
   126  	ErrorTypeForbidden ErrorType = "FieldValueForbidden"
   127  	// ErrorTypeTooLong is used to report that the given value is too long.
   128  	// This is similar to ErrorTypeInvalid, but the error will not include the
   129  	// too-long value.  See TooLong().
   130  	ErrorTypeTooLong ErrorType = "FieldValueTooLong"
   131  	// ErrorTypeTooMany is used to report "too many". This is used to
   132  	// report that a given list has too many items. This is similar to FieldValueTooLong,
   133  	// but the error indicates quantity instead of length.
   134  	ErrorTypeTooMany ErrorType = "FieldValueTooMany"
   135  	// ErrorTypeInternal is used to report other errors that are not related
   136  	// to user input.  See InternalError().
   137  	ErrorTypeInternal ErrorType = "InternalError"
   138  	// ErrorTypeTypeInvalid is for the value did not match the schema type for that field
   139  	ErrorTypeTypeInvalid ErrorType = "FieldValueTypeInvalid"
   140  )
   141  
   142  // String converts a ErrorType into its corresponding canonical error message.
   143  func (t ErrorType) String() string {
   144  	switch t {
   145  	case ErrorTypeNotFound:
   146  		return "Not found"
   147  	case ErrorTypeRequired:
   148  		return "Required value"
   149  	case ErrorTypeDuplicate:
   150  		return "Duplicate value"
   151  	case ErrorTypeInvalid:
   152  		return "Invalid value"
   153  	case ErrorTypeNotSupported:
   154  		return "Unsupported value"
   155  	case ErrorTypeForbidden:
   156  		return "Forbidden"
   157  	case ErrorTypeTooLong:
   158  		return "Too long"
   159  	case ErrorTypeTooMany:
   160  		return "Too many"
   161  	case ErrorTypeInternal:
   162  		return "Internal error"
   163  	case ErrorTypeTypeInvalid:
   164  		return "Invalid value"
   165  	default:
   166  		panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
   167  	}
   168  }
   169  
   170  // TypeInvalid returns a *Error indicating "type is invalid"
   171  func TypeInvalid(field *Path, value interface{}, detail string) *Error {
   172  	return &Error{ErrorTypeTypeInvalid, field.String(), value, detail}
   173  }
   174  
   175  // NotFound returns a *Error indicating "value not found".  This is
   176  // used to report failure to find a requested value (e.g. looking up an ID).
   177  func NotFound(field *Path, value interface{}) *Error {
   178  	return &Error{ErrorTypeNotFound, field.String(), value, ""}
   179  }
   180  
   181  // Required returns a *Error indicating "value required".  This is used
   182  // to report required values that are not provided (e.g. empty strings, null
   183  // values, or empty arrays).
   184  func Required(field *Path, detail string) *Error {
   185  	return &Error{ErrorTypeRequired, field.String(), "", detail}
   186  }
   187  
   188  // Duplicate returns a *Error indicating "duplicate value".  This is
   189  // used to report collisions of values that must be unique (e.g. names or IDs).
   190  func Duplicate(field *Path, value interface{}) *Error {
   191  	return &Error{ErrorTypeDuplicate, field.String(), value, ""}
   192  }
   193  
   194  // Invalid returns a *Error indicating "invalid value".  This is used
   195  // to report malformed values (e.g. failed regex match, too long, out of bounds).
   196  func Invalid(field *Path, value interface{}, detail string) *Error {
   197  	return &Error{ErrorTypeInvalid, field.String(), value, detail}
   198  }
   199  
   200  // NotSupported returns a *Error indicating "unsupported value".
   201  // This is used to report unknown values for enumerated fields (e.g. a list of
   202  // valid values).
   203  func NotSupported(field *Path, value interface{}, validValues []string) *Error {
   204  	detail := ""
   205  	if len(validValues) > 0 {
   206  		quotedValues := make([]string, len(validValues))
   207  		for i, v := range validValues {
   208  			quotedValues[i] = strconv.Quote(v)
   209  		}
   210  		detail = "supported values: " + strings.Join(quotedValues, ", ")
   211  	}
   212  	return &Error{ErrorTypeNotSupported, field.String(), value, detail}
   213  }
   214  
   215  // Forbidden returns a *Error indicating "forbidden".  This is used to
   216  // report valid (as per formatting rules) values which would be accepted under
   217  // some conditions, but which are not permitted by current conditions (e.g.
   218  // security policy).
   219  func Forbidden(field *Path, detail string) *Error {
   220  	return &Error{ErrorTypeForbidden, field.String(), "", detail}
   221  }
   222  
   223  // TooLong returns a *Error indicating "too long".  This is used to
   224  // report that the given value is too long.  This is similar to
   225  // Invalid, but the returned error will not include the too-long
   226  // value.
   227  func TooLong(field *Path, value interface{}, maxLength int) *Error {
   228  	return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
   229  }
   230  
   231  // TooLongMaxLength returns a *Error indicating "too long".  This is used to
   232  // report that the given value is too long.  This is similar to
   233  // Invalid, but the returned error will not include the too-long
   234  // value. If maxLength is negative, no max length will be included in the message.
   235  func TooLongMaxLength(field *Path, value interface{}, maxLength int) *Error {
   236  	var msg string
   237  	if maxLength >= 0 {
   238  		msg = fmt.Sprintf("may not be longer than %d", maxLength)
   239  	} else {
   240  		msg = "value is too long"
   241  	}
   242  	return &Error{ErrorTypeTooLong, field.String(), value, msg}
   243  }
   244  
   245  // TooMany returns a *Error indicating "too many". This is used to
   246  // report that a given list has too many items. This is similar to TooLong,
   247  // but the returned error indicates quantity instead of length.
   248  func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
   249  	var msg string
   250  
   251  	if maxQuantity >= 0 {
   252  		msg = fmt.Sprintf("must have at most %d items", maxQuantity)
   253  	} else {
   254  		msg = "has too many items"
   255  	}
   256  
   257  	var actual interface{}
   258  	if actualQuantity >= 0 {
   259  		actual = actualQuantity
   260  	} else {
   261  		actual = omitValue
   262  	}
   263  
   264  	return &Error{ErrorTypeTooMany, field.String(), actual, msg}
   265  }
   266  
   267  // InternalError returns a *Error indicating "internal error".  This is used
   268  // to signal that an error was found that was not directly related to user
   269  // input.  The err argument must be non-nil.
   270  func InternalError(field *Path, err error) *Error {
   271  	return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
   272  }
   273  
   274  // ErrorList holds a set of Errors.  It is plausible that we might one day have
   275  // non-field errors in this same umbrella package, but for now we don't, so
   276  // we can keep it simple and leave ErrorList here.
   277  type ErrorList []*Error
   278  
   279  // NewErrorTypeMatcher returns an errors.Matcher that returns true
   280  // if the provided error is a Error and has the provided ErrorType.
   281  func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
   282  	return func(err error) bool {
   283  		if e, ok := err.(*Error); ok {
   284  			return e.Type == t
   285  		}
   286  		return false
   287  	}
   288  }
   289  
   290  // ToAggregate converts the ErrorList into an errors.Aggregate.
   291  func (list ErrorList) ToAggregate() utilerrors.Aggregate {
   292  	if len(list) == 0 {
   293  		return nil
   294  	}
   295  	errs := make([]error, 0, len(list))
   296  	errorMsgs := sets.NewString()
   297  	for _, err := range list {
   298  		msg := fmt.Sprintf("%v", err)
   299  		if errorMsgs.Has(msg) {
   300  			continue
   301  		}
   302  		errorMsgs.Insert(msg)
   303  		errs = append(errs, err)
   304  	}
   305  	return utilerrors.NewAggregate(errs)
   306  }
   307  
   308  func fromAggregate(agg utilerrors.Aggregate) ErrorList {
   309  	errs := agg.Errors()
   310  	list := make(ErrorList, len(errs))
   311  	for i := range errs {
   312  		list[i] = errs[i].(*Error)
   313  	}
   314  	return list
   315  }
   316  
   317  // Filter removes items from the ErrorList that match the provided fns.
   318  func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
   319  	err := utilerrors.FilterOut(list.ToAggregate(), fns...)
   320  	if err == nil {
   321  		return nil
   322  	}
   323  	// FilterOut takes an Aggregate and returns an Aggregate
   324  	return fromAggregate(err.(utilerrors.Aggregate))
   325  }