Skip to content

Commit 06a2b1d

Browse files
committed
Further address feedback
1 parent 04b3075 commit 06a2b1d

File tree

1 file changed

+27
-34
lines changed
  • keps/sig-api-machinery/1929-built-in-default

1 file changed

+27
-34
lines changed

keps/sig-api-machinery/1929-built-in-default/README.md

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,11 @@ some defaulting that doesn't require to be done as code though.
176176
It is important to us that the OpenAPI definition of a type should
177177
result 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
202204
fields with null values if a default is available, or the field will be
203205
removed if no default exists, but only when the openapi nullable flag is
204206
missing 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
207209
nullable are happy with null values. Also, people can remove the
208210
nullable attribute if they want null values to start being removed. This
209211
will 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

260262
Because built-in types are serialized and deserialized as Go structures,
261263
there are semantics that are imposed on us. The two main ones are:
262264
1. We can't differentiate between null, unspecified and zero-value,
263265
2. 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

266268
The first point is solved by the removed nulls described in the previous
267269
section (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
270272
forbidden on non-pointer structs (pointer structs can behave as
271273
expected) 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

277279
Computed defaults (through defaulting functions) will not be supported
278280
as 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

408410
For CRDs, invalid zero-values are omitted, otherwise the objects would
409411
technically 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:
418420
type 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

440436
This 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
456449
Which, 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
468461
built-ins would look like this:
469462
```golang
470463
func GenerateDefault(obj *Object) {

0 commit comments

Comments
 (0)