Skip to content

Commit

Permalink
cover additional types in unstructured roundtrip test
Browse files Browse the repository at this point in the history
Co-authored-by: Ben Luddy <bluddy@redhat.com>
  • Loading branch information
sanchezl and benluddy committed Oct 11, 2024
1 parent 3d6c99e commit aaa7364
Show file tree
Hide file tree
Showing 10 changed files with 1,440 additions and 213 deletions.
2 changes: 1 addition & 1 deletion pkg/api/testing/unstructured_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func TestRoundtripToUnstructured(t *testing.T) {
}
}

roundtrip.RoundtripToUnstructured(t, legacyscheme.Scheme, FuzzerFuncs, skipped)
roundtrip.RoundtripToUnstructured(t, legacyscheme.Scheme, FuzzerFuncs, skipped, nil)
}

func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion staging/src/k8s.io/apiextensions-apiserver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ godebug default=go1.23

require (
github.com/emicklei/go-restful/v3 v3.11.0
github.com/fxamacker/cbor/v2 v2.7.0
github.com/gogo/protobuf v1.3.2
github.com/google/cel-go v0.21.0
github.com/google/gnostic-models v0.6.8
Expand Down Expand Up @@ -53,7 +54,6 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,42 @@ import (
"bytes"
"errors"

cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
"k8s.io/apimachinery/pkg/util/json"
)

var jsTrue = []byte("true")
var jsFalse = []byte("false")

// The CBOR parsing related constants and functions below are not exported so they can be
// easily removed at a future date when the CBOR library provides equivalent functionality.

type cborMajorType int

const (
// https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1
cborUnsignedInteger cborMajorType = 0
cborNegativeInteger cborMajorType = 1
cborByteString cborMajorType = 2
cborTextString cborMajorType = 3
cborArray cborMajorType = 4
cborMap cborMajorType = 5
cborTag cborMajorType = 6
cborOther cborMajorType = 7
)

const (
// from https://www.rfc-editor.org/rfc/rfc8949.html#name-jump-table-for-initial-byte.
// additionally, see https://www.rfc-editor.org/rfc/rfc8949.html#section-3.3-5.
cborFalseValue = 0xf4
cborTrueValue = 0xf5
cborNullValue = 0xf6
)

func cborType(b byte) cborMajorType {
return cborMajorType(b >> 5)
}

func (s JSONSchemaPropsOrBool) MarshalJSON() ([]byte, error) {
if s.Schema != nil {
return json.Marshal(s.Schema)
Expand Down Expand Up @@ -59,6 +89,39 @@ func (s *JSONSchemaPropsOrBool) UnmarshalJSON(data []byte) error {
return nil
}

func (s JSONSchemaPropsOrBool) MarshalCBOR() ([]byte, error) {
if s.Schema != nil {
return cbor.Marshal(s.Schema)
}
return cbor.Marshal(s.Allows)
}

func (s *JSONSchemaPropsOrBool) UnmarshalCBOR(data []byte) error {
switch {
case len(data) == 0:
// ideally we would avoid modifying *s here, but we are matching the behavior of UnmarshalJSON
*s = JSONSchemaPropsOrBool{}
return nil
case cborType(data[0]) == cborMap:
var p JSONSchemaProps
if err := cbor.Unmarshal(data, &p); err != nil {
return err
}
*s = JSONSchemaPropsOrBool{Allows: true, Schema: &p}
return nil
case data[0] == cborTrueValue:
*s = JSONSchemaPropsOrBool{Allows: true}
return nil
case data[0] == cborFalseValue:
*s = JSONSchemaPropsOrBool{Allows: false}
return nil
default:
// ideally, this case would not also capture a null input value,
// but we are matching the behavior of the UnmarshalJSON
return errors.New("boolean or JSON schema expected")
}
}

func (s JSONSchemaPropsOrStringArray) MarshalJSON() ([]byte, error) {
if len(s.Property) > 0 {
return json.Marshal(s.Property)
Expand Down Expand Up @@ -91,6 +154,40 @@ func (s *JSONSchemaPropsOrStringArray) UnmarshalJSON(data []byte) error {
return nil
}

func (s JSONSchemaPropsOrStringArray) MarshalCBOR() ([]byte, error) {
if len(s.Property) > 0 {
return cbor.Marshal(s.Property)
}
if s.Schema != nil {
return cbor.Marshal(s.Schema)
}
return cbor.Marshal(nil)
}

func (s *JSONSchemaPropsOrStringArray) UnmarshalCBOR(data []byte) error {
if len(data) > 0 && cborType(data[0]) == cborArray {
var a []string
if err := cbor.Unmarshal(data, &a); err != nil {
return err
}
*s = JSONSchemaPropsOrStringArray{Property: a}
return nil
}
if len(data) > 0 && cborType(data[0]) == cborMap {
var p JSONSchemaProps
if err := cbor.Unmarshal(data, &p); err != nil {
return err
}
*s = JSONSchemaPropsOrStringArray{Schema: &p}
return nil
}
// At this point we either have: empty data, a null value, or an
// unexpected type. In order to match the behavior of the existing
// UnmarshalJSON, no error is returned and *s is overwritten here.
*s = JSONSchemaPropsOrStringArray{}
return nil
}

func (s JSONSchemaPropsOrArray) MarshalJSON() ([]byte, error) {
if len(s.JSONSchemas) > 0 {
return json.Marshal(s.JSONSchemas)
Expand Down Expand Up @@ -120,6 +217,37 @@ func (s *JSONSchemaPropsOrArray) UnmarshalJSON(data []byte) error {
return nil
}

func (s JSONSchemaPropsOrArray) MarshalCBOR() ([]byte, error) {
if len(s.JSONSchemas) > 0 {
return cbor.Marshal(s.JSONSchemas)
}
return cbor.Marshal(s.Schema)
}

func (s *JSONSchemaPropsOrArray) UnmarshalCBOR(data []byte) error {
if len(data) > 0 && cborType(data[0]) == cborMap {
var p JSONSchemaProps
if err := cbor.Unmarshal(data, &p); err != nil {
return err
}
*s = JSONSchemaPropsOrArray{Schema: &p}
return nil
}
if len(data) > 0 && cborType(data[0]) == cborArray {
var a []JSONSchemaProps
if err := cbor.Unmarshal(data, &a); err != nil {
return err
}
*s = JSONSchemaPropsOrArray{JSONSchemas: a}
return nil
}
// At this point we either have: empty data, a null value, or an
// unexpected type. In order to match the behavior of the existing
// UnmarshalJSON, no error is returned and *s is overwritten here.
*s = JSONSchemaPropsOrArray{}
return nil
}

func (s JSON) MarshalJSON() ([]byte, error) {
if len(s.Raw) > 0 {
return s.Raw, nil
Expand All @@ -134,3 +262,34 @@ func (s *JSON) UnmarshalJSON(data []byte) error {
}
return nil
}

func (s JSON) MarshalCBOR() ([]byte, error) {
// Note that non-semantic whitespace is lost during the transcoding performed here.
// We do not forsee this to be a problem given the current known uses of this type.
// Other limitations that arise when roundtripping JSON via dynamic clients also apply
// here, for example: insignificant whitespace handling, number handling, and map key ordering.
if len(s.Raw) == 0 {
return []byte{cborNullValue}, nil
}
var u any
if err := json.Unmarshal(s.Raw, &u); err != nil {
return nil, err
}
return cbor.Marshal(u)
}

func (s *JSON) UnmarshalCBOR(data []byte) error {
if len(data) == 0 || data[0] == cborNullValue {
return nil
}
var u any
if err := cbor.Unmarshal(data, &u); err != nil {
return err
}
raw, err := json.Marshal(u)
if err != nil {
return err
}
s.Raw = raw
return nil
}
Loading

0 comments on commit aaa7364

Please sign in to comment.