@@ -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,10 +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+ Non-pointer scalar fields also have a specific unmarshalling mechanism in Go,
408+ since they always have a value .
407409
408410For CRDs, invalid zero-values are omitted, otherwise the objects would
409411technically be invalid. We can safely default these field if they are
@@ -418,24 +420,18 @@ Both these cases will be handled properly, here's an example:
418420type Object struct {
419421 // This field has an invalid zero-value, empty string is not a
420422 // 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- //
427423 // +default="default-name"
428- Name string ` json:",omitempty"`
424+ Name string ` json:"name ,omitempty"`
429425
430- // This field is defaulted to 0 with no defaulter .
426+ // This field would be defaulted to 0 without a default .
431427 // +default=0
432428 Defaulted int
433429}
434430```
435431
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 .
432+ Non-pointer scalar fields that have a default must be omitempty (unless
433+ their default is the zero-value). Kubebuilder and built-in defaulting
434+ generators should both require this .
439435
440436This would generate the following OpenAPI:
441437``` yaml
@@ -445,26 +441,23 @@ Object:
445441 invalid :
446442 type : string
447443 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
451444 defaulted :
452- type : int
445+ type : integer
453446 default : false
454447` ` `
455448
456449Which, for both CRD types and built-in types, would generate the following behavior:
457450` ` ` python
458451>>> default('{}')
459- {"entry": {"name ": "default-name", "number": 0}}
460- >>> default('{name : other-name}')
461- {"name ": "other-name", "number ": 0}
452+ {"entry": {"invalid ": "default-name", "number": 0}}
453+ >>> default('{invalid : other-name}')
454+ {"invalid ": "other-name", "defaulted ": 0}
462455# For built-in, additionally, we would have:
463- >>> default('{name : ""}')
464- {"name ": "default-name", "number ": 0}
456+ >>> default('{invalid : ""}')
457+ {"invalid ": "default-name", "defaulted ": 0}
465458```
466459
467- Note that a simplified, and non-cached version of the generated code for
460+ Note that a simplified version, which deserializes on every call, of the generated code for
468461built-ins would look like this:
469462``` golang
470463func GenerateDefault (obj *Object ) {
0 commit comments