2 Copyright 2014 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
25 utilerrors "k8s.io/apimachinery/pkg/util/errors"
26 "k8s.io/apimachinery/pkg/util/sets"
29 // Error is an implementation of the 'error' interface, which represents a
30 // field-level validation error.
38 var _ error = &Error{}
40 // Error implements the error interface.
41 func (v *Error) Error() string {
42 return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
45 // ErrorBody returns the error message without the field name. This is useful
46 // for building nice-looking higher-level error reporting.
47 func (v *Error) ErrorBody() string {
50 case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
54 valueType := reflect.TypeOf(value)
55 if value == nil || valueType == nil {
57 } else if valueType.Kind() == reflect.Ptr {
58 if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
61 value = reflectValue.Elem().Interface()
64 switch t := value.(type) {
65 case int64, int32, float64, float32, bool:
66 // use simple printer for simple types
67 s = fmt.Sprintf("%s: %v", v.Type, value)
69 s = fmt.Sprintf("%s: %q", v.Type, t)
71 // anything that defines String() is better than raw struct
72 s = fmt.Sprintf("%s: %s", v.Type, t.String())
74 // fallback to raw struct
75 // TODO: internal types have panic guards against json.Marshalling to prevent
76 // accidental use of internal types in external serialized form. For now, use
77 // %#v, although it would be better to show a more expressive output in the future
78 s = fmt.Sprintf("%s: %#v", v.Type, value)
81 if len(v.Detail) != 0 {
82 s += fmt.Sprintf(": %s", v.Detail)
87 // ErrorType is a machine readable value providing more detail about why
88 // a field is invalid. These values are expected to match 1-1 with
89 // CauseType in api/types.go.
92 // TODO: These values are duplicated in api/types.go, but there's a circular dep. Fix it.
94 // ErrorTypeNotFound is used to report failure to find a requested value
95 // (e.g. looking up an ID). See NotFound().
96 ErrorTypeNotFound ErrorType = "FieldValueNotFound"
97 // ErrorTypeRequired is used to report required values that are not
98 // provided (e.g. empty strings, null values, or empty arrays). See
100 ErrorTypeRequired ErrorType = "FieldValueRequired"
101 // ErrorTypeDuplicate is used to report collisions of values that must be
102 // unique (e.g. unique IDs). See Duplicate().
103 ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
104 // ErrorTypeInvalid is used to report malformed values (e.g. failed regex
105 // match, too long, out of bounds). See Invalid().
106 ErrorTypeInvalid ErrorType = "FieldValueInvalid"
107 // ErrorTypeNotSupported is used to report unknown values for enumerated
108 // fields (e.g. a list of valid values). See NotSupported().
109 ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
110 // ErrorTypeForbidden is used to report valid (as per formatting rules)
111 // values which would be accepted under some conditions, but which are not
112 // permitted by the current conditions (such as security policy). See
114 ErrorTypeForbidden ErrorType = "FieldValueForbidden"
115 // ErrorTypeTooLong is used to report that the given value is too long.
116 // This is similar to ErrorTypeInvalid, but the error will not include the
117 // too-long value. See TooLong().
118 ErrorTypeTooLong ErrorType = "FieldValueTooLong"
119 // ErrorTypeInternal is used to report other errors that are not related
120 // to user input. See InternalError().
121 ErrorTypeInternal ErrorType = "InternalError"
124 // String converts a ErrorType into its corresponding canonical error message.
125 func (t ErrorType) String() string {
127 case ErrorTypeNotFound:
129 case ErrorTypeRequired:
130 return "Required value"
131 case ErrorTypeDuplicate:
132 return "Duplicate value"
133 case ErrorTypeInvalid:
134 return "Invalid value"
135 case ErrorTypeNotSupported:
136 return "Unsupported value"
137 case ErrorTypeForbidden:
139 case ErrorTypeTooLong:
141 case ErrorTypeInternal:
142 return "Internal error"
144 panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
148 // NotFound returns a *Error indicating "value not found". This is
149 // used to report failure to find a requested value (e.g. looking up an ID).
150 func NotFound(field *Path, value interface{}) *Error {
151 return &Error{ErrorTypeNotFound, field.String(), value, ""}
154 // Required returns a *Error indicating "value required". This is used
155 // to report required values that are not provided (e.g. empty strings, null
156 // values, or empty arrays).
157 func Required(field *Path, detail string) *Error {
158 return &Error{ErrorTypeRequired, field.String(), "", detail}
161 // Duplicate returns a *Error indicating "duplicate value". This is
162 // used to report collisions of values that must be unique (e.g. names or IDs).
163 func Duplicate(field *Path, value interface{}) *Error {
164 return &Error{ErrorTypeDuplicate, field.String(), value, ""}
167 // Invalid returns a *Error indicating "invalid value". This is used
168 // to report malformed values (e.g. failed regex match, too long, out of bounds).
169 func Invalid(field *Path, value interface{}, detail string) *Error {
170 return &Error{ErrorTypeInvalid, field.String(), value, detail}
173 // NotSupported returns a *Error indicating "unsupported value".
174 // This is used to report unknown values for enumerated fields (e.g. a list of
176 func NotSupported(field *Path, value interface{}, validValues []string) *Error {
178 if validValues != nil && len(validValues) > 0 {
179 quotedValues := make([]string, len(validValues))
180 for i, v := range validValues {
181 quotedValues[i] = strconv.Quote(v)
183 detail = "supported values: " + strings.Join(quotedValues, ", ")
185 return &Error{ErrorTypeNotSupported, field.String(), value, detail}
188 // Forbidden returns a *Error indicating "forbidden". This is used to
189 // report valid (as per formatting rules) values which would be accepted under
190 // some conditions, but which are not permitted by current conditions (e.g.
192 func Forbidden(field *Path, detail string) *Error {
193 return &Error{ErrorTypeForbidden, field.String(), "", detail}
196 // TooLong returns a *Error indicating "too long". This is used to
197 // report that the given value is too long. This is similar to
198 // Invalid, but the returned error will not include the too-long
200 func TooLong(field *Path, value interface{}, maxLength int) *Error {
201 return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d characters", maxLength)}
204 // InternalError returns a *Error indicating "internal error". This is used
205 // to signal that an error was found that was not directly related to user
206 // input. The err argument must be non-nil.
207 func InternalError(field *Path, err error) *Error {
208 return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
211 // ErrorList holds a set of Errors. It is plausible that we might one day have
212 // non-field errors in this same umbrella package, but for now we don't, so
213 // we can keep it simple and leave ErrorList here.
214 type ErrorList []*Error
216 // NewErrorTypeMatcher returns an errors.Matcher that returns true
217 // if the provided error is a Error and has the provided ErrorType.
218 func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
219 return func(err error) bool {
220 if e, ok := err.(*Error); ok {
227 // ToAggregate converts the ErrorList into an errors.Aggregate.
228 func (list ErrorList) ToAggregate() utilerrors.Aggregate {
229 errs := make([]error, 0, len(list))
230 errorMsgs := sets.NewString()
231 for _, err := range list {
232 msg := fmt.Sprintf("%v", err)
233 if errorMsgs.Has(msg) {
236 errorMsgs.Insert(msg)
237 errs = append(errs, err)
239 return utilerrors.NewAggregate(errs)
242 func fromAggregate(agg utilerrors.Aggregate) ErrorList {
244 list := make(ErrorList, len(errs))
245 for i := range errs {
246 list[i] = errs[i].(*Error)
251 // Filter removes items from the ErrorList that match the provided fns.
252 func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
253 err := utilerrors.FilterOut(list.ToAggregate(), fns...)
257 // FilterOut takes an Aggregate and returns an Aggregate
258 return fromAggregate(err.(utilerrors.Aggregate))