1
2
3
4
19
20 package field
21
22 import (
23 "fmt"
24 "reflect"
25 "strconv"
26 "strings"
27
28 utilerrors "sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/errors"
29 "sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/sets"
30 )
31
32
33
34 type Error struct {
35 Type ErrorType
36 Field string
37 BadValue interface{}
38 Detail string
39 }
40
41 var _ error = &Error{}
42
43
44 func (v *Error) Error() string {
45 return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
46 }
47
48
49
50 func (v *Error) ErrorBody() string {
51 var s string
52 switch v.Type {
53 case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
54 s = v.Type.String()
55 default:
56 value := v.BadValue
57 valueType := reflect.TypeOf(value)
58 if value == nil || valueType == nil {
59 value = "null"
60 } else if valueType.Kind() == reflect.Ptr {
61 if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
62 value = "null"
63 } else {
64 value = reflectValue.Elem().Interface()
65 }
66 }
67 switch t := value.(type) {
68 case int64, int32, float64, float32, bool:
69
70 s = fmt.Sprintf("%s: %v", v.Type, value)
71 case string:
72 s = fmt.Sprintf("%s: %q", v.Type, t)
73 case fmt.Stringer:
74
75 s = fmt.Sprintf("%s: %s", v.Type, t.String())
76 default:
77
78
79
80
81 s = fmt.Sprintf("%s: %#v", v.Type, value)
82 }
83 }
84 if len(v.Detail) != 0 {
85 s += fmt.Sprintf(": %s", v.Detail)
86 }
87 return s
88 }
89
90
91
92
93 type ErrorType string
94
95
96 const (
97
98
99 ErrorTypeNotFound ErrorType = "FieldValueNotFound"
100
101
102
103 ErrorTypeRequired ErrorType = "FieldValueRequired"
104
105
106 ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
107
108
109 ErrorTypeInvalid ErrorType = "FieldValueInvalid"
110
111
112 ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
113
114
115
116
117 ErrorTypeForbidden ErrorType = "FieldValueForbidden"
118
119
120
121 ErrorTypeTooLong ErrorType = "FieldValueTooLong"
122
123
124
125 ErrorTypeTooMany ErrorType = "FieldValueTooMany"
126
127
128 ErrorTypeInternal ErrorType = "InternalError"
129 )
130
131
132 func (t ErrorType) String() string {
133 switch t {
134 case ErrorTypeNotFound:
135 return "Not found"
136 case ErrorTypeRequired:
137 return "Required value"
138 case ErrorTypeDuplicate:
139 return "Duplicate value"
140 case ErrorTypeInvalid:
141 return "Invalid value"
142 case ErrorTypeNotSupported:
143 return "Unsupported value"
144 case ErrorTypeForbidden:
145 return "Forbidden"
146 case ErrorTypeTooLong:
147 return "Too long"
148 case ErrorTypeTooMany:
149 return "Too many"
150 case ErrorTypeInternal:
151 return "Internal error"
152 default:
153 panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
154 }
155 }
156
157
158
159 func NotFound(field *Path, value interface{}) *Error {
160 return &Error{ErrorTypeNotFound, field.String(), value, ""}
161 }
162
163
164
165
166 func Required(field *Path, detail string) *Error {
167 return &Error{ErrorTypeRequired, field.String(), "", detail}
168 }
169
170
171
172 func Duplicate(field *Path, value interface{}) *Error {
173 return &Error{ErrorTypeDuplicate, field.String(), value, ""}
174 }
175
176
177
178 func Invalid(field *Path, value interface{}, detail string) *Error {
179 return &Error{ErrorTypeInvalid, field.String(), value, detail}
180 }
181
182
183
184
185 func NotSupported(field *Path, value interface{}, validValues []string) *Error {
186 detail := ""
187 if validValues != nil && len(validValues) > 0 {
188 quotedValues := make([]string, len(validValues))
189 for i, v := range validValues {
190 quotedValues[i] = strconv.Quote(v)
191 }
192 detail = "supported values: " + strings.Join(quotedValues, ", ")
193 }
194 return &Error{ErrorTypeNotSupported, field.String(), value, detail}
195 }
196
197
198
199
200
201 func Forbidden(field *Path, detail string) *Error {
202 return &Error{ErrorTypeForbidden, field.String(), "", detail}
203 }
204
205
206
207
208
209 func TooLong(field *Path, value interface{}, maxLength int) *Error {
210 return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
211 }
212
213
214
215
216 func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
217 return &Error{ErrorTypeTooMany, field.String(), actualQuantity, fmt.Sprintf("must have at most %d items", maxQuantity)}
218 }
219
220
221
222
223 func InternalError(field *Path, err error) *Error {
224 return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
225 }
226
227
228
229
230 type ErrorList []*Error
231
232
233
234 func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
235 return func(err error) bool {
236 if e, ok := err.(*Error); ok {
237 return e.Type == t
238 }
239 return false
240 }
241 }
242
243
244 func (list ErrorList) ToAggregate() utilerrors.Aggregate {
245 errs := make([]error, 0, len(list))
246 errorMsgs := sets.NewString()
247 for _, err := range list {
248 msg := fmt.Sprintf("%v", err)
249 if errorMsgs.Has(msg) {
250 continue
251 }
252 errorMsgs.Insert(msg)
253 errs = append(errs, err)
254 }
255 return utilerrors.NewAggregate(errs)
256 }
257
258 func fromAggregate(agg utilerrors.Aggregate) ErrorList {
259 errs := agg.Errors()
260 list := make(ErrorList, len(errs))
261 for i := range errs {
262 list[i] = errs[i].(*Error)
263 }
264 return list
265 }
266
267
268 func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
269 err := utilerrors.FilterOut(list.ToAggregate(), fns...)
270 if err == nil {
271 return nil
272 }
273
274 return fromAggregate(err.(utilerrors.Aggregate))
275 }
276
View as plain text