@@ -176,9 +176,11 @@ some defaulting that doesn't require to be done as code though.
176176It is important to us that the OpenAPI definition of a type should
177177result in the same behavior, whether the type is built-in or a CRD.
178178
179- In order to do that, we need to change how CRDs are defaulted, and
180- create a defaulting mechanism for built-in that would behave the same
181- way.
179+ In order to do that, we need to change how CRDs are defaulted, and place
180+ specific requirements on definition of built-in types (specifically
181+ around omitempty on non-pointer scalars, and an implicit default of {}
182+ on non-pointer struct fields), and create a defaulting mechanism for
183+ built-in that would behave the same way.
182184
183185### CRD Normalization
184186
@@ -202,8 +204,8 @@ get defaulted if a default is available. We propose to start defaulting
202204fields with null values if a default is available, or the field will be
203205removed if no default exists, but only when the openapi nullable flag is
204206missing or false. This means that will start accepting inputs that were
205- rejected in the past (there is code that would work with a new server
206- but not with an old server) . We assume that existing APIs that use
207+ rejected in the past. There is code that would work with a new server
208+ but not with an old server. We assume that existing APIs that use
207209nullable are happy with null values. Also, people can remove the
208210nullable attribute if they want null values to start being removed. This
209211will help aligning CRDs and built-in types (there are no ways to set
@@ -255,13 +257,13 @@ while still allowing more complex expressions:
255257// +default=[{"port": 80, "name": "http"}]
256258```
257259
258- ### Built-in Defaulting
260+ ### Defaulting mechanisms
259261
260262Because built-in types are serialized and deserialized as Go structures,
261263there are semantics that are imposed on us. The two main ones are:
2622641 . We can't differentiate between null, unspecified and zero-value,
2632652 . non-pointer structs are always present (and hence have to be
264- recursively defaulted), they also can't be omitEmpty 'd.
266+ recursively defaulted), they also can't be omitempty 'd.
265267
266268The first point is solved by the removed nulls described in the previous
267269section (and invalid zero-values can't be present in objects), and the
@@ -270,9 +272,9 @@ default. To address that, we're proposing that the `default` marker is
270272forbidden on non-pointer structs (pointer structs can behave as
271273expected) and the default field of the openapi is automatically set to
272274` {} ` . This only applies to OpenAPI generated from built-in types or from
273- kubebuilder. CRDs can still have any defaulting they have for
274- structures , but we strongly suggest that OpenAPI generated from Go types
275- should follow that semantics.
275+ kubebuilder. Existing CRD defaults defined for type=object properties
276+ will continue to be supported , but we strongly suggest that OpenAPI
277+ generated from Go types should follow that semantics.
276278
277279Computed defaults (through defaulting functions) will not be supported
278280as a declarative marker and will continue being supported through the
@@ -400,14 +402,10 @@ which would have the following behavior, both for CRD types and built-in types:
400402{"entry": {"name": "other-name", "number": 0}}
401403```
402404
403- #### Non-pointer fields
405+ #### Non-pointer scalar fields
404406
405- Non-pointer fields are also important, since they have a specific
406- unmarshalling mechanism in Go.
407-
408- For CRDs, invalid zero-values are omitted, otherwise the objects would
409- technically be invalid. We can safely default these field if they are
410- unspecified.
407+ Non-pointer scalar fields also have a specific unmarshalling mechanism in Go,
408+ since they always have a value.
411409
412410For built-ins, we can differentiate two cases:
4134111 . The zero-value for the field is invalid (e.g. if empty string is not a valid value)
@@ -418,24 +416,18 @@ Both these cases will be handled properly, here's an example:
418416type Object struct {
419417 // This field has an invalid zero-value, empty string is not a
420418 // valid string.
421- //
422- // If this is a CRD, there would be a kubebuilder specific tag, and
423- // if this is a built-in, there would be a specific validation function,
424- // but basically:
425- // +minLength=1
426- //
427419 // +default="default-name"
428- Name string ` json:",omitempty"`
420+ Name string ` json:"name ,omitempty"`
429421
430- // This field is defaulted to 0 with no defaulter .
422+ // This field would be defaulted to 0 without a default .
431423 // +default=0
432424 Defaulted int
433425}
434426```
435427
436- Non-pointer fields that have a default must be omitEmpty. Kubebuilder
437- and built-in defaulting generators should both prevent these from
438- happening .
428+ Non-pointer scalar fields that have a default must be omitempty (unless
429+ their default is the zero-value). Kubebuilder and built-in defaulting
430+ generators should both require this .
439431
440432This would generate the following OpenAPI:
441433``` yaml
@@ -445,26 +437,23 @@ Object:
445437 invalid :
446438 type : string
447439 default : " default-name"
448- # This field would only be present for CRDs, for built-in,
449- # the empty value would be accepted and treated as missing.
450- minLength : 1
451440 defaulted :
452- type : int
441+ type : integer
453442 default : false
454443` ` `
455444
456445Which, for both CRD types and built-in types, would generate the following behavior:
457446` ` ` python
458447>>> default('{}')
459- {"entry": {"name ": "default-name", "number": 0}}
460- >>> default('{name : other-name}')
461- {"name ": "other-name", "number ": 0}
448+ {"entry": {"invalid ": "default-name", "number": 0}}
449+ >>> default('{invalid : other-name}')
450+ {"invalid ": "other-name", "defaulted ": 0}
462451# For built-in, additionally, we would have:
463- >>> default('{name : ""}')
464- {"name ": "default-name", "number ": 0}
452+ >>> default('{invalid : ""}')
453+ {"invalid ": "default-name", "defaulted ": 0}
465454```
466455
467- Note that a simplified, and non-cached version of the generated code for
456+ Note that a simplified version, which deserializes on every call, of the generated code for
468457built-ins would look like this:
469458``` golang
470459func GenerateDefault (obj *Object ) {
0 commit comments