diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go index cd78b1df266..a71a487f9ec 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go @@ -23,14 +23,39 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes" ) +// Marshal serializes a value to CBOR. If there is more than one way to encode the value, it will +// make the same choice as the CBOR implementation of runtime.Serializer. +// +// Note: Support for CBOR is at an alpha stage. If the value (or, for composite types, any of its +// nested values) implement any of the interfaces encoding.TextMarshaler, encoding.TextUnmarshaler, +// encoding/json.Marshaler, or encoding/json.Unmarshaler, a non-nil error will be returned unless +// the value also implements the corresponding CBOR interfaces. This limitation will ultimately be +// removed in favor of automatic transcoding to CBOR. func Marshal(src interface{}) ([]byte, error) { + if err := modes.RejectCustomMarshalers(src); err != nil { + return nil, err + } return modes.Encode.Marshal(src) } +// Unmarshal deserializes from CBOR into an addressable value. If there is more than one way to +// unmarshal a value, it will make the same choice as the CBOR implementation of runtime.Serializer. +// +// Note: Support for CBOR is at an alpha stage. If the value (or, for composite types, any of its +// nested values) implement any of the interfaces encoding.TextMarshaler, encoding.TextUnmarshaler, +// encoding/json.Marshaler, or encoding/json.Unmarshaler, a non-nil error will be returned unless +// the value also implements the corresponding CBOR interfaces. This limitation will ultimately be +// removed in favor of automatic transcoding to CBOR. func Unmarshal(src []byte, dst interface{}) error { + if err := modes.RejectCustomMarshalers(dst); err != nil { + return err + } return modes.Decode.Unmarshal(src, dst) } +// Diagnose accepts well-formed CBOR bytes and returns a string representing the same data item in +// human-readable diagnostic notation (RFC 8949 Section 8). The diagnostic notation is not meant to +// be parsed. func Diagnose(src []byte) (string, error) { return modes.Diagnostic.Diagnose(src) } diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct_test.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct_test.go new file mode 100644 index 00000000000..2986ee3f435 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct_test.go @@ -0,0 +1,81 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package direct_test + +import ( + "encoding" + "encoding/json" + "fmt" + "reflect" + "testing" + + "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct" +) + +var _ json.Marshaler = CustomJSONMarshaler{} + +type CustomJSONMarshaler struct{} + +func (CustomJSONMarshaler) MarshalJSON() ([]byte, error) { + panic("unimplemented") +} + +var _ json.Unmarshaler = CustomJSONUnmarshaler{} + +type CustomJSONUnmarshaler struct{} + +func (CustomJSONUnmarshaler) UnmarshalJSON([]byte) error { + panic("unimplemented") +} + +var _ encoding.TextMarshaler = CustomTextMarshaler{} + +type CustomTextMarshaler struct{} + +func (CustomTextMarshaler) MarshalText() ([]byte, error) { + panic("unimplemented") +} + +var _ encoding.TextUnmarshaler = CustomTextUnmarshaler{} + +type CustomTextUnmarshaler struct{} + +func (CustomTextUnmarshaler) UnmarshalText([]byte) error { + panic("unimplemented") +} + +func TestRejectsCustom(t *testing.T) { + for _, tc := range []struct { + value interface{} + iface reflect.Type + }{ + {value: CustomJSONMarshaler{}, iface: reflect.TypeFor[json.Marshaler]()}, + {value: CustomJSONUnmarshaler{}, iface: reflect.TypeFor[json.Unmarshaler]()}, + {value: CustomTextMarshaler{}, iface: reflect.TypeFor[encoding.TextMarshaler]()}, + {value: CustomTextUnmarshaler{}, iface: reflect.TypeFor[encoding.TextUnmarshaler]()}, + } { + t.Run(fmt.Sprintf("%T", tc.value), func(t *testing.T) { + want := fmt.Sprintf("unable to serialize %T: %T implements %s without corresponding cbor interface", tc.value, tc.value, tc.iface.String()) + if _, err := direct.Marshal(tc.value); err == nil || err.Error() != want { + t.Errorf("want error: %q, got: %v", want, err) + } + if err := direct.Unmarshal(nil, tc.value); err == nil || err.Error() != want { + t.Errorf("want error: %q, got: %v", want, err) + } + }) + } +}