Skip to content

Commit c473f6d

Browse files
committed
Address feedback around open questions and PRR
1 parent dee6e69 commit c473f6d

File tree

1 file changed

+77
-26
lines changed
  • keps/sig-api-machinery/1027-api-unions

1 file changed

+77
-26
lines changed

keps/sig-api-machinery/1027-api-unions/README.md

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
- [Risks and Mitigations](#risks-and-mitigations)
1717
- [Design Details](#design-details)
1818
- [Discriminator Field](#discriminator-field)
19-
- [Go tags](#go-tags)
19+
- [Go Markers](#go-markers)
2020
- [OpenAPI](#openapi)
2121
- [Normalization and Validation](#normalization-and-validation)
2222
- [Open Questions](#open-questions)
@@ -132,10 +132,12 @@ functions (e.g. `pkg/apis/<group>/validation/validation.go`).
132132
best match the intent of the client, despite the client potentially having
133133
incomplete information
134134

135+
Another goal is to migrate at least one existing union to the new semantics in
136+
order to verify that migration is possible.
137+
135138
### Non-Goals
136139

137-
The focus of this KEP is on providing unified validation and normalization of
138-
new union types. Migrating existing unions away from their bespoke validation
140+
Migrating all existing unions away from their bespoke validation
139141
logic (e.g validation functions), is an explicit non-goal and will be pursued in
140142
a separate KEP or later release.
141143

@@ -248,7 +250,7 @@ kubebuilder types):
248250
- `// +unionMember=<memberName>,discriminatedBy=<discriminatorName>` before a field means that this
249251
field is a member of a union. The `<memberName>` is the name of the field that must be set as the discriminator value. It defaults to the json representation of the field name if not specified. The `<discriminatorName>` is optional if there
250252
is only union in a struct and required if there are multiple unions per
251-
struct. It should be the json representation of the field tagged with
253+
struct. It should be the go representation of the field tagged with
252254
`unionDiscriminator`.
253255

254256
Note unions can't span across multiple go structures (all the fields that are part of a union has to be together in the
@@ -349,7 +351,13 @@ discriminator.
349351

350352
This means two things:
351353
1. when the server receives a request with a discriminator set to a
352-
given field, it should clear out any other fields that are set.
354+
given field, and multiple member fields are set it should:
355+
a. If the discriminator has been modified from the one set in the old object,
356+
the server should clear all fields except the one pointed to by the discriminator.
357+
b. If the discriminator is unchanged but a new member field has been set (in
358+
addition to the existing one that has not been cleared), it should error
359+
loudly that the client must change the discriminator if it changes any union
360+
member fields.
353361
2. when the server receives a request with a discriminator set to a given field,
354362
but that given field is empty, the server should fail with a clear error
355363
message.
@@ -371,19 +379,36 @@ Objects must be validated AFTER the normalization process.
371379
A few open questions exist with the design that need to be resolved with SIG API
372380
Machinery before moving forward.
373381

374-
1. When a server receives an update request with empty data in the member fields
375-
but a validly set discriminator pointing to the object's union member field
376-
that was previously set, should the server error and inform the client that
377-
the field pointed to by the discriminator is currently empty, or should the
378-
server retain the previous value it knows was set to the field being pointed
379-
to by the discriminator?
382+
1. What do we do when the discriminator is set (to the previously set value),
383+
but all union member fields are null? This likely implies that the client cleared
384+
the union member unintentionally, given that the discriminator value remains.
385+
Should the server:
386+
a. Error loudly and inform the client that the field pointed to by the
387+
discriminator is null?
388+
b. Retain the previously set member field, present on the old object, absent
389+
from the new object, but pointed to by the discriminator?
380390
2. What should the value of the discriminator be when no field in the union is
381-
to be set. A couple options inclued an empty string, an field common to all
382-
union (e.g. "NONE"), or a field specified on a per union basis.
391+
to be set (i.e for optional unions).
392+
a. mandate that the discriminator is the empty string for all optional unions?
393+
b. make the field specified on a per union basis (defined in the go markers)?
394+
c. mandate that the discriminator be unset (thus make the discriminator an
395+
optional field for optional unios)?
383396
3. How should a server behave when it receives a union with multiple fields set
384-
and the discriminator pointing to one of them? Reject the request, accept the
385-
request but don't clear the fields, or automatically clear the fields not
386-
specified by the discriminator.
397+
and the discriminator modified to point to the newly set member field?
398+
a. reject the request,
399+
b. accept the request but don't clear any fields
400+
c. accept the request and clear all the fields except the one chosen by the
401+
discriminator
402+
4. Given that we want to migrate at least one existing union to use the new
403+
semantics, which union should we do? Must we upgrade both a discriminated and
404+
non-discriminated union for alpha or is only upgrading an existing
405+
discriminated union sufficient?
406+
5. What should we default the discriminator value of a member field to if not
407+
specified in the go markers?
408+
a. the json representation of the field name
409+
b. the go representation of the field name
410+
Should we even give users the ability to define it or just stick to whatever
411+
we decide the default is?
387412

388413
### Test Plan
389414

@@ -480,7 +505,7 @@ We expect no non-infra related flakes in the last month as a GA graduation crite
480505

481506
- CRDs can be created with union fields and are properly validated when
482507
created/updated.
483-
- Existing unions that have discriminators, are properly tagged and validated via
508+
- At least one existing unions that has discriminators, is properly tagged and validated via
484509
union validation
485510
- Existing unions that don't have discriminators do not break when upgraded.
486511

@@ -498,8 +523,11 @@ enhancement:
498523
cluster required to make on upgrade, in order to make use of the enhancement?
499524
-->
500525

501-
At first, Only new union types will follow the prescribed guinance, so no upgrades/downgrades are possible for types
502-
that don't exist yet.
526+
Turning the flag on for alpha just enables different runtime codepaths (i.e.
527+
performing the unified union validation and normalization)
528+
529+
Any schema markers (added by CRD authors or propagated from tags on built-in
530+
types) will appear in the schema, but not do anything if the flag is off.
503531

504532
### Version Skew Strategy
505533

@@ -520,6 +548,13 @@ See test matrix and commentary about discriminators. It clearly documents how
520548
the server will use the discriminator to understand the client's intention even
521549
if the client is not aware of all union fields because of version skew.
522550

551+
Skew with alpha flag on/off shouldn't make much of a difference.
552+
* Objects created with the union semantics, but applied to a cluster with the
553+
alpha flag off will simply not perform union validation and normalization.
554+
* Objects created without union semantics will simply not trigger union
555+
validation and normalization (regardless of whether the server has the alpha
556+
enabled or disabled).
557+
523558
## Production Readiness Review Questionnaire
524559

525560
<!--
@@ -565,15 +600,15 @@ well as the [existing list] of feature gates.
565600
- [x] Feature gate (also fill in values in `kep.yaml`)
566601
- Feature gate name: APIUnions
567602
- Components depending on the feature gate: kube-apiserver
568-
603+
569604
Request handlers in the api server will call into union validation and
570605
normalization function from the structured-merge-diff repo when feature is
571606
enabled.
572607

573608

574609
###### Does enabling the feature change any default behavior?
575610

576-
No
611+
No, since all built in unions are currently validated in other ways.
577612

578613
<!--
579614
Any change of default behavior may be surprising to users or break existing
@@ -597,7 +632,8 @@ Yes, requests will simply skip union validation and normalization.
597632

598633
###### What happens if we reenable the feature if it was previously rolled back?
599634

600-
Requests will resume perform union validation and normalization
635+
Requests will resume performing union validation and normalization; there is no
636+
persistent state behind this feature.
601637

602638
###### Are there any tests for feature enablement/disablement?
603639

@@ -669,7 +705,13 @@ previous answers based on experience in the field.
669705

670706
###### How can an operator determine if the feature is in use by workloads?
671707

672-
Simply creating and updating objects with union fields.
708+
For builtins in alpha, it won't be possible to break clients since turning on vs
709+
off will validate the same thing via different code paths.
710+
711+
For CRDs, you can see if they have the new union markers. If the CRD has no
712+
other validation mechanism, turning off the flag may result in CRs accepting
713+
invalid input.
714+
673715
<!--
674716
Ideally, this should be a metric. Operations against the Kubernetes API (e.g.,
675717
checking if there are objects with field X set) may be a last resort. Avoid
@@ -678,10 +720,13 @@ logs or events for this purpose.
678720

679721
###### How can someone using this feature know that it is working for their instance?
680722

681-
1. Create a new CRD with a union field
723+
1. Create a new CRD with a union field (and no other validation mechanism)
682724
2. Apply the CRD
683725
3. Create a CR with an invalid union (multiple fields set, no discriminator
684726
set), see if the CR is rejected via union validation
727+
728+
When we write the e2e test, a standard union CRD and test CR will be obtainable
729+
for users to test on their instance.
685730
<!--
686731
For instance, if this is a pod-related feature, it should be possible to determine if the feature is functioning properly
687732
for each individual pod.
@@ -827,7 +872,9 @@ Describe them, providing:
827872

828873
###### Will enabling / using this feature result in increasing time taken by any operations covered by existing SLIs/SLOs?
829874

830-
No
875+
In GA (maybe beta), we might expect resource reduction/reliability improvement,
876+
since this removes a need for webhooks.
877+
831878
<!--
832879
Look at the [existing SLIs/SLOs].
833880
@@ -839,7 +886,11 @@ Think about adding additional work or introducing new steps in between
839886

840887
###### Will enabling / using this feature result in non-negligible increase of resource usage (CPU, RAM, disk, IO, ...) in any components?
841888

842-
No
889+
New validation and normalization logic should be negligible given that the
890+
functions will be in the same SMD path currently used by SSA code.
891+
892+
We will have benchmarking to validate this assumption.
893+
843894
<!--
844895
Things to keep in mind include: additional in-memory state, additional
845896
non-trivial computations, excessive access to disks (including increased log

0 commit comments

Comments
 (0)