Skip to content

Commit 8291980

Browse files
committed
KEP-4222: Specify handling of JSON-only custom marshalers.
1 parent ed7065f commit 8291980

File tree

1 file changed

+49
-0
lines changed
  • keps/sig-api-machinery/4222-cbor-serializer

1 file changed

+49
-0
lines changed

keps/sig-api-machinery/4222-cbor-serializer/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ tags, and then generate with `hack/update-toc.sh`.
104104
- [Unit tests](#unit-tests)
105105
- [Integration tests](#integration-tests)
106106
- [e2e tests](#e2e-tests)
107+
- [Custom JSON Marshalers](#custom-json-marshalers)
107108
- [Graduation Criteria](#graduation-criteria)
108109
- [Alpha](#alpha)
109110
- [Beta](#beta)
@@ -752,6 +753,48 @@ We expect no non-infra related flakes in the last month as a GA graduation crite
752753

753754
- request and response content-type negotiation with 1.17 sample API server
754755

756+
### Custom JSON Marshalers
757+
758+
If a type implements json.Marshaler or json.Unmarshaler without corresponding CBOR behaviors,
759+
serializing values of that type to and from CBOR using default behaviors risks mangling the data.
760+
761+
As an example, consider the structure of a marshalled
762+
[IntOrString](https://pkg.go.dev/k8s.io/apimachinery/pkg/util/intstr#IntOrString) with the custom
763+
behavior versus the default behavior:
764+
765+
| Go | Custom | Default |
766+
|------------------------------------------|---------|---------------------------------------|
767+
| IntOrString{Type: Int, IntVal: 7} | 7 | {"IntVal":7,"StrVal":"","Type:":0} |
768+
| IntOrString{Type: String, StrVal: "foo"} | "foo" | {"IntVal":0,"StrVal":"foo","Type:":1} |
769+
| IntOrString{Type: -1} | <error> | {"IntVal":0,"StrVal":"","Type:":-1} |
770+
771+
Imagine a similar type is declared out-of-tree. It has a similar implementation of `json.Marshaler`,
772+
but not corresponding custom implementation for CBOR. From this type, a CRD and typed client are
773+
generated. This typed client is used in a program to write to a custom resource, using JSON to
774+
encode the request body as either a JSON number or a JSON string. On the server side, the request
775+
body is decoded into an Unstructured object, and within that object, the IntOrString value is
776+
represented by either a `string` or an `int64`.
777+
778+
Now imagine that the same request is repeated, but with CBOR as the negotiated content type of the
779+
request body, and that the CBOR serializer implementation _does not_ recognize types that implement
780+
`json.Marshaler` or `json.Unmarshaler`. By changing the request content type from JSON to CBOR, the
781+
actual bytes of the request body represent a structurally different object. Referencing the table
782+
above, instead of the "Custom" encoding, the encoded CBOR would look like the "Default" encoding.
783+
784+
On the server side, the value is represented within the decoded Unstructured as a
785+
`map[string]interface{}` with three keys, `"IntVal"`, `"StrVal`", and `"Type"`. A change in the
786+
request encoding resulted in a structural change to the object the client intended to send.
787+
788+
The CBOR serializer must not use the default behaviors to marshal and unmarshal values that
789+
implement only custom JSON behaviors. Rejecting them with an error is a minimum requirement for
790+
alpha, since it prevents corruption. This would support in-tree types, server-side custom resource
791+
serialization, and typical dynamic client usage. A second alpha release will support these types
792+
automatically by invoking the JSON methods and transcoding to or from CBOR.
793+
794+
All of the above also applies to types implementing `encoding.TextMarshaler` (which is used if
795+
implemented unless `json.Marshaler` is also implemented) and `encoding.TextUnmarshaler` (which is
796+
used if implemented when the input is a JSON string unless `json.Unmarshaler` is also implemented).
797+
755798
### Graduation Criteria
756799

757800
<!--
@@ -824,11 +867,17 @@ in back-to-back releases.
824867
- Client generation updated to support CBOR behind client-side gates.
825868
- Runtime gating mechanism added to client-go.
826869
- Maintenance of CBOR library is understood.
870+
- Types that implement json.Marshaler or json.Unmarshaler without corresponding custom CBOR
871+
behaviors are either rejected with an error on Encode and Decode or automatically transcoded from
872+
JSON.
827873

828874
#### Beta
829875

830876
- Review of nondeterministic encoding mode and final decision on whether to keep
831877
or remove it.
878+
- To support rollback from beta to alpha, at least one alpha release has supported automatic
879+
transcoding of types that implement json.Marshaler or json.Unmarshaler without corresponding
880+
custom CBOR behaviors.
832881

833882
#### GA
834883

0 commit comments

Comments
 (0)