@@ -17,9 +17,9 @@ limitations under the License.
17
17
package markers
18
18
19
19
import (
20
- "fmt"
21
-
22
20
"encoding/json"
21
+ "fmt"
22
+ "math"
23
23
24
24
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
25
25
@@ -37,7 +37,7 @@ const (
37
37
// reusable and writing complex validations on slice items.
38
38
var ValidationMarkers = mustMakeAllWithPrefix ("kubebuilder:validation" , markers .DescribesField ,
39
39
40
- // integer markers
40
+ // numeric markers
41
41
42
42
Maximum (0 ),
43
43
Minimum (0 ),
@@ -123,11 +123,19 @@ func init() {
123
123
124
124
// +controllertools:marker:generateHelp:category="CRD validation"
125
125
// Maximum specifies the maximum numeric value that this field can have.
126
- type Maximum int
126
+ type Maximum float64
127
+
128
+ func (m Maximum ) Value () float64 {
129
+ return float64 (m )
130
+ }
127
131
128
132
// +controllertools:marker:generateHelp:category="CRD validation"
129
- // Minimum specifies the minimum numeric value that this field can have. Negative integers are supported.
130
- type Minimum int
133
+ // Minimum specifies the minimum numeric value that this field can have. Negative numbers are supported.
134
+ type Minimum float64
135
+
136
+ func (m Minimum ) Value () float64 {
137
+ return float64 (m )
138
+ }
131
139
132
140
// +controllertools:marker:generateHelp:category="CRD validation"
133
141
// ExclusiveMinimum indicates that the minimum is "up to" but not including that value.
@@ -139,7 +147,11 @@ type ExclusiveMaximum bool
139
147
140
148
// +controllertools:marker:generateHelp:category="CRD validation"
141
149
// MultipleOf specifies that this field must have a numeric value that's a multiple of this one.
142
- type MultipleOf int
150
+ type MultipleOf float64
151
+
152
+ func (m MultipleOf ) Value () float64 {
153
+ return float64 (m )
154
+ }
143
155
144
156
// +controllertools:marker:generateHelp:category="CRD validation"
145
157
// MaxLength specifies the maximum length for this string.
@@ -252,6 +264,14 @@ type XIntOrString struct{}
252
264
// to be used only as a last resort.
253
265
type Schemaless struct {}
254
266
267
+ func hasNumericType (schema * apiext.JSONSchemaProps ) bool {
268
+ return schema .Type == "integer" || schema .Type == "number"
269
+ }
270
+
271
+ func isIntegral (value float64 ) bool {
272
+ return value == math .Trunc (value ) && ! math .IsNaN (value ) && ! math .IsInf (value , 0 )
273
+ }
274
+
255
275
// +controllertools:marker:generateHelp:category="CRD validation"
256
276
// XValidation marks a field as requiring a value for which a given
257
277
// expression evaluates to true.
@@ -264,40 +284,60 @@ type XValidation struct {
264
284
}
265
285
266
286
func (m Maximum ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
267
- if schema .Type != "integer" {
268
- return fmt .Errorf ("must apply maximum to an integer" )
287
+ if ! hasNumericType (schema ) {
288
+ return fmt .Errorf ("must apply maximum to a numeric value, found %s" , schema .Type )
289
+ }
290
+
291
+ if schema .Type == "integer" && ! isIntegral (m .Value ()) {
292
+ return fmt .Errorf ("cannot apply non-integral maximum validation (%v) to integer value" , m .Value ())
269
293
}
270
- val := float64 (m )
294
+
295
+ val := m .Value ()
271
296
schema .Maximum = & val
272
297
return nil
273
298
}
299
+
274
300
func (m Minimum ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
275
- if schema .Type != "integer" {
276
- return fmt .Errorf ("must apply minimum to an integer" )
301
+ if ! hasNumericType (schema ) {
302
+ return fmt .Errorf ("must apply minimum to a numeric value, found %s" , schema .Type )
303
+ }
304
+
305
+ if schema .Type == "integer" && ! isIntegral (m .Value ()) {
306
+ return fmt .Errorf ("cannot apply non-integral minimum validation (%v) to integer value" , m .Value ())
277
307
}
278
- val := float64 (m )
308
+
309
+ val := m .Value ()
279
310
schema .Minimum = & val
280
311
return nil
281
312
}
313
+
282
314
func (m ExclusiveMaximum ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
283
- if schema . Type != "integer" {
284
- return fmt .Errorf ("must apply exclusivemaximum to an integer" )
315
+ if ! hasNumericType ( schema ) {
316
+ return fmt .Errorf ("must apply exclusivemaximum to a numeric value, found %s" , schema . Type )
285
317
}
286
318
schema .ExclusiveMaximum = bool (m )
287
319
return nil
288
320
}
321
+
289
322
func (m ExclusiveMinimum ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
290
- if schema . Type != "integer" {
291
- return fmt .Errorf ("must apply exclusiveminimum to an integer" )
323
+ if ! hasNumericType ( schema ) {
324
+ return fmt .Errorf ("must apply exclusiveminimum to a numeric value, found %s" , schema . Type )
292
325
}
326
+
293
327
schema .ExclusiveMinimum = bool (m )
294
328
return nil
295
329
}
330
+
296
331
func (m MultipleOf ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
297
- if schema . Type != "integer" {
298
- return fmt .Errorf ("must apply multipleof to an integer" )
332
+ if ! hasNumericType ( schema ) {
333
+ return fmt .Errorf ("must apply multipleof to a numeric value, found %s" , schema . Type )
299
334
}
300
- val := float64 (m )
335
+
336
+ if schema .Type == "integer" && ! isIntegral (m .Value ()) {
337
+ return fmt .Errorf ("cannot apply non-integral multipleof validation (%v) to integer value" , m .Value ())
338
+ }
339
+
340
+ val := m .Value ()
301
341
schema .MultipleOf = & val
302
342
return nil
303
343
}
@@ -310,6 +350,7 @@ func (m MaxLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
310
350
schema .MaxLength = & val
311
351
return nil
312
352
}
353
+
313
354
func (m MinLength ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
314
355
if schema .Type != "string" {
315
356
return fmt .Errorf ("must apply minlength to a string" )
@@ -318,6 +359,7 @@ func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
318
359
schema .MinLength = & val
319
360
return nil
320
361
}
362
+
321
363
func (m Pattern ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
322
364
// Allow string types or IntOrStrings. An IntOrString will still
323
365
// apply the pattern validation when a string is detected, the pattern
@@ -337,6 +379,7 @@ func (m MaxItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
337
379
schema .MaxItems = & val
338
380
return nil
339
381
}
382
+
340
383
func (m MinItems ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
341
384
if schema .Type != "array" {
342
385
return fmt .Errorf ("must apply minitems to an array" )
@@ -345,6 +388,7 @@ func (m MinItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
345
388
schema .MinItems = & val
346
389
return nil
347
390
}
391
+
348
392
func (m UniqueItems ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
349
393
if schema .Type != "array" {
350
394
return fmt .Errorf ("must apply uniqueitems to an array" )
@@ -388,6 +432,7 @@ func (m Enum) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
388
432
schema .Enum = vals
389
433
return nil
390
434
}
435
+
391
436
func (m Format ) ApplyToSchema (schema * apiext.JSONSchemaProps ) error {
392
437
schema .Format = string (m )
393
438
return nil
0 commit comments