Skip to content

Commit 9fdab3d

Browse files
authored
Merge pull request #4711 from benluddy/4222-custom-json-marshalers
KEP-4222: Specify handling of JSON-only custom marshalers.
2 parents 4371010 + 8291980 commit 9fdab3d

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)
@@ -751,6 +752,48 @@ We expect no non-infra related flakes in the last month as a GA graduation crite
751752

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

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

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

827873
#### Beta
828874

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

832881
#### GA
833882

0 commit comments

Comments
 (0)