mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Add CBOR Serializer implementation.
This commit is contained in:
parent
d76d7a1e7a
commit
066421f108
@ -17,5 +17,228 @@ limitations under the License.
|
|||||||
package cbor
|
package cbor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/fxamacker/cbor/v2"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
|
||||||
|
util "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type metaFactory interface {
|
||||||
|
// Interpret should return the version and kind of the wire-format of the object.
|
||||||
|
Interpret(data []byte) (*schema.GroupVersionKind, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultMetaFactory struct{}
|
||||||
|
|
||||||
|
func (mf *defaultMetaFactory) Interpret(data []byte) (*schema.GroupVersionKind, error) {
|
||||||
|
var tm metav1.TypeMeta
|
||||||
|
// The input is expected to include additional map keys besides apiVersion and kind, so use
|
||||||
|
// lax mode for decoding into TypeMeta.
|
||||||
|
if err := modes.DecodeLax.Unmarshal(data, &tm); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to determine group/version/kind: %w", err)
|
||||||
|
}
|
||||||
|
actual := tm.GetObjectKind().GroupVersionKind()
|
||||||
|
return &actual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Serializer interface {
|
||||||
|
runtime.Serializer
|
||||||
|
recognizer.RecognizingDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Serializer = &serializer{}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
strict bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func Strict(s bool) Option {
|
||||||
|
return func(opts *options) {
|
||||||
|
opts.strict = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type serializer struct {
|
||||||
|
metaFactory metaFactory
|
||||||
|
creater runtime.ObjectCreater
|
||||||
|
typer runtime.ObjectTyper
|
||||||
|
options options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) Serializer {
|
||||||
|
return newSerializer(&defaultMetaFactory{}, creater, typer, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSerializer(metaFactory metaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) *serializer {
|
||||||
|
s := &serializer{
|
||||||
|
metaFactory: metaFactory,
|
||||||
|
creater: creater,
|
||||||
|
typer: typer,
|
||||||
|
}
|
||||||
|
for _, o := range options {
|
||||||
|
o(&s.options)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serializer) Identifier() runtime.Identifier {
|
||||||
|
return "cbor"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serializer) Encode(obj runtime.Object, w io.Writer) error {
|
||||||
|
if _, err := w.Write(selfDescribedCBOR); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e := modes.Encode.NewEncoder(w)
|
||||||
|
if u, ok := obj.(runtime.Unstructured); ok {
|
||||||
|
return e.Encode(u.UnstructuredContent())
|
||||||
|
}
|
||||||
|
return e.Encode(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gvkWithDefaults returns group kind and version defaulting from provided default
|
||||||
|
func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
|
||||||
|
if len(actual.Kind) == 0 {
|
||||||
|
actual.Kind = defaultGVK.Kind
|
||||||
|
}
|
||||||
|
if len(actual.Version) == 0 && len(actual.Group) == 0 {
|
||||||
|
actual.Group = defaultGVK.Group
|
||||||
|
actual.Version = defaultGVK.Version
|
||||||
|
}
|
||||||
|
if len(actual.Version) == 0 && actual.Group == defaultGVK.Group {
|
||||||
|
actual.Version = defaultGVK.Version
|
||||||
|
}
|
||||||
|
return actual
|
||||||
|
}
|
||||||
|
|
||||||
|
// diagnose returns the diagnostic encoding of a well-formed CBOR data item.
|
||||||
|
func diagnose(data []byte) string {
|
||||||
|
diag, err := modes.Diagnostic.Diagnose(data)
|
||||||
|
if err != nil {
|
||||||
|
// Since the input must already be well-formed CBOR, converting it to diagnostic
|
||||||
|
// notation should not fail.
|
||||||
|
util.HandleError(err)
|
||||||
|
|
||||||
|
return hex.EncodeToString(data)
|
||||||
|
}
|
||||||
|
return diag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serializer) unmarshal(data []byte, into interface{}) (strict, lax error) {
|
||||||
|
if u, ok := into.(runtime.Unstructured); ok {
|
||||||
|
var content map[string]interface{}
|
||||||
|
defer func() {
|
||||||
|
// TODO: The UnstructuredList implementation of SetUnstructuredContent is
|
||||||
|
// not identical to what unstructuredJSONScheme does: (1) it retains the
|
||||||
|
// "items" key in its Object field, and (2) it does not infer a singular
|
||||||
|
// Kind from the list's Kind and populate omitted apiVersion/kind for all
|
||||||
|
// entries in Items.
|
||||||
|
u.SetUnstructuredContent(content)
|
||||||
|
}()
|
||||||
|
into = &content
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.options.strict {
|
||||||
|
return nil, modes.DecodeLax.Unmarshal(data, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := modes.Decode.Unmarshal(data, into)
|
||||||
|
// TODO: UnknownFieldError is ambiguous. It only provides the index of the first problematic
|
||||||
|
// map entry encountered and does not indicate which map the index refers to.
|
||||||
|
var unknownField *cbor.UnknownFieldError
|
||||||
|
if errors.As(err, &unknownField) {
|
||||||
|
// Unlike JSON, there are no strict errors in CBOR for duplicate map keys. CBOR maps
|
||||||
|
// with duplicate keys are considered invalid according to the spec and are rejected
|
||||||
|
// entirely.
|
||||||
|
return runtime.NewStrictDecodingError([]error{unknownField}), modes.DecodeLax.Unmarshal(data, into)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serializer) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
|
||||||
|
// A preliminary pass over the input to obtain the actual GVK is redundant on a successful
|
||||||
|
// decode into Unstructured.
|
||||||
|
if _, ok := into.(runtime.Unstructured); ok {
|
||||||
|
if _, unmarshalErr := s.unmarshal(data, into); unmarshalErr != nil {
|
||||||
|
actual, interpretErr := s.metaFactory.Interpret(data)
|
||||||
|
if interpretErr != nil {
|
||||||
|
return nil, nil, interpretErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if gvk != nil {
|
||||||
|
*actual = gvkWithDefaults(*actual, *gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, actual, unmarshalErr
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := into.GetObjectKind().GroupVersionKind()
|
||||||
|
if len(actual.Kind) == 0 {
|
||||||
|
return nil, &actual, runtime.NewMissingKindErr(diagnose(data))
|
||||||
|
}
|
||||||
|
if len(actual.Version) == 0 {
|
||||||
|
return nil, &actual, runtime.NewMissingVersionErr(diagnose(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return into, &actual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := s.metaFactory.Interpret(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if gvk != nil {
|
||||||
|
*actual = gvkWithDefaults(*actual, *gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if into != nil {
|
||||||
|
types, _, err := s.typer.ObjectKinds(into)
|
||||||
|
if err != nil {
|
||||||
|
return nil, actual, err
|
||||||
|
}
|
||||||
|
*actual = gvkWithDefaults(*actual, types[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actual.Kind) == 0 {
|
||||||
|
return nil, actual, runtime.NewMissingKindErr(diagnose(data))
|
||||||
|
}
|
||||||
|
if len(actual.Version) == 0 {
|
||||||
|
return nil, actual, runtime.NewMissingVersionErr(diagnose(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
|
||||||
|
if err != nil {
|
||||||
|
return nil, actual, err
|
||||||
|
}
|
||||||
|
|
||||||
|
strict, err := s.unmarshal(data, obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, actual, err
|
||||||
|
}
|
||||||
|
return obj, actual, strict
|
||||||
|
}
|
||||||
|
|
||||||
|
// selfDescribedCBOR is the CBOR encoding of the head of tag number 55799. This tag, specified in
|
||||||
|
// RFC 8949 Section 3.4.6 "Self-Described CBOR", encloses all output from the encoder, has no
|
||||||
|
// special semantics, and is used as a magic number to recognize CBOR-encoded data items.
|
||||||
|
//
|
||||||
|
// See https://www.rfc-editor.org/rfc/rfc8949.html#name-self-described-cbor.
|
||||||
|
var selfDescribedCBOR = []byte{0xd9, 0xd9, 0xf7}
|
||||||
|
|
||||||
|
func (s *serializer) RecognizesData(data []byte) (ok, unknown bool, err error) {
|
||||||
|
return bytes.HasPrefix(data, selfDescribedCBOR), false, nil
|
||||||
|
}
|
||||||
|
@ -0,0 +1,475 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The tests in this package focus on the correctness of its implementation of
|
||||||
|
// runtime.Serializer. The specific behavior of marshaling Go values to CBOR bytes and back is
|
||||||
|
// tested in the ./internal/modes package, which is used both by the Serializer implementation and
|
||||||
|
// the package-scoped Marshal/Unmarshal functions in the ./direct package.
|
||||||
|
package cbor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRecognizesData(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
in []byte
|
||||||
|
recognizes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: nil,
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{},
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xd9},
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xd9, 0xd9},
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xd9, 0xd9, 0xf7},
|
||||||
|
recognizes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xff, 0xff, 0xff},
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xd9, 0xd9, 0xf7, 0x01, 0x02, 0x03},
|
||||||
|
recognizes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: []byte{0xff, 0xff, 0xff, 0x01, 0x02, 0x03},
|
||||||
|
recognizes: false,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(hex.EncodeToString(tc.in), func(t *testing.T) {
|
||||||
|
s := NewSerializer(nil, nil)
|
||||||
|
recognizes, unknown, err := s.RecognizesData(tc.in)
|
||||||
|
if recognizes != tc.recognizes {
|
||||||
|
t.Errorf("expected recognized to be %t, got %t", tc.recognizes, recognizes)
|
||||||
|
}
|
||||||
|
if unknown {
|
||||||
|
t.Error("expected unknown to be false, got true")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubWriter struct {
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w stubWriter) Write([]byte) (int, error) {
|
||||||
|
return w.n, w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// anyObject wraps arbitrary concrete values to be encoded or decoded.
|
||||||
|
type anyObject struct {
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p anyObject) GetObjectKind() schema.ObjectKind {
|
||||||
|
return schema.EmptyObjectKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (anyObject) DeepCopyObject() runtime.Object {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p anyObject) MarshalCBOR() ([]byte, error) {
|
||||||
|
return modes.Encode.Marshal(p.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *anyObject) UnmarshalCBOR(in []byte) error {
|
||||||
|
return modes.Decode.Unmarshal(in, &p.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncode(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
in runtime.Object
|
||||||
|
assertOnWriter func() (io.Writer, func(*testing.T))
|
||||||
|
assertOnError func(*testing.T, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "io error writing self described cbor tag",
|
||||||
|
assertOnWriter: func() (io.Writer, func(*testing.T)) {
|
||||||
|
return stubWriter{err: io.ErrShortWrite}, func(*testing.T) {}
|
||||||
|
},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !errors.Is(err, io.ErrShortWrite) {
|
||||||
|
t.Errorf("expected io.ErrShortWrite, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "output enclosed by self-described CBOR tag",
|
||||||
|
in: anyObject{},
|
||||||
|
assertOnWriter: func() (io.Writer, func(*testing.T)) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
return &b, func(t *testing.T) {
|
||||||
|
if !bytes.HasPrefix(b.Bytes(), []byte{0xd9, 0xd9, 0xf7}) {
|
||||||
|
t.Errorf("expected output to have prefix 0xd9d9f7: 0x%x", b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
s := NewSerializer(nil, nil)
|
||||||
|
w, assertOnWriter := tc.assertOnWriter()
|
||||||
|
err := s.Encode(tc.in, w)
|
||||||
|
tc.assertOnError(t, err)
|
||||||
|
assertOnWriter(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
options []Option
|
||||||
|
data []byte
|
||||||
|
gvk *schema.GroupVersionKind
|
||||||
|
metaFactory metaFactory
|
||||||
|
typer runtime.ObjectTyper
|
||||||
|
creater runtime.ObjectCreater
|
||||||
|
into runtime.Object
|
||||||
|
expectedObj runtime.Object
|
||||||
|
expectedGVK *schema.GroupVersionKind
|
||||||
|
assertOnError func(*testing.T, error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "error determining gvk",
|
||||||
|
metaFactory: stubMetaFactory{err: errors.New("test")},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err == nil || err.Error() != "test" {
|
||||||
|
t.Errorf("expected error \"test\", got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typer does not recognize into",
|
||||||
|
gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
typer: notRegisteredTyper{},
|
||||||
|
into: &anyObject{},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsNotRegisteredError(err) {
|
||||||
|
t.Errorf("expected NotRegisteredError, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gvk from type of into",
|
||||||
|
data: []byte{0xf6},
|
||||||
|
gvk: &schema.GroupVersionKind{},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
|
||||||
|
into: &anyObject{},
|
||||||
|
expectedObj: &anyObject{},
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strict mode strict error",
|
||||||
|
options: []Option{Strict(true)},
|
||||||
|
data: []byte{0xa1, 0x61, 'z', 0x01}, // {'z': 1}
|
||||||
|
gvk: &schema.GroupVersionKind{},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
|
||||||
|
into: &metav1.PartialObjectMetadata{},
|
||||||
|
expectedObj: &metav1.PartialObjectMetadata{},
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsStrictDecodingError(err) {
|
||||||
|
t.Errorf("expected StrictDecodingError, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no strict mode no strict error",
|
||||||
|
data: []byte{0xa1, 0x61, 'z', 0x01}, // {'z': 1}
|
||||||
|
gvk: &schema.GroupVersionKind{},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
|
||||||
|
into: &metav1.PartialObjectMetadata{},
|
||||||
|
expectedObj: &metav1.PartialObjectMetadata{},
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown error from typer on into",
|
||||||
|
gvk: &schema.GroupVersionKind{},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
typer: stubTyper{err: errors.New("test")},
|
||||||
|
into: &anyObject{},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err == nil || err.Error() != "test" {
|
||||||
|
t.Errorf("expected error \"test\", got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing kind",
|
||||||
|
gvk: &schema.GroupVersionKind{Version: "v"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Version: "v"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsMissingKind(err) {
|
||||||
|
t.Errorf("expected MissingKind, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing version",
|
||||||
|
gvk: &schema.GroupVersionKind{Kind: "k"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Kind: "k"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsMissingVersion(err) {
|
||||||
|
t.Errorf("expected MissingVersion, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "creater error",
|
||||||
|
gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
creater: stubCreater{err: errors.New("test")},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err == nil || err.Error() != "test" {
|
||||||
|
t.Errorf("expected error \"test\", got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unmarshal error",
|
||||||
|
data: nil, // EOF
|
||||||
|
gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
creater: stubCreater{obj: &anyObject{}},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
t.Errorf("expected EOF, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strict mode unmarshal error",
|
||||||
|
options: []Option{Strict(true)},
|
||||||
|
data: nil, // EOF
|
||||||
|
gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
creater: stubCreater{obj: &anyObject{}},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
t.Errorf("expected EOF, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "into unstructured unmarshal error",
|
||||||
|
data: nil, // EOF
|
||||||
|
gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
|
||||||
|
into: &unstructured.Unstructured{},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
t.Errorf("expected EOF, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "into unstructured missing kind",
|
||||||
|
data: []byte("\xa1\x6aapiVersion\x61v"),
|
||||||
|
into: &unstructured.Unstructured{},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Version: "v"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsMissingKind(err) {
|
||||||
|
t.Errorf("expected MissingKind, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "into unstructured missing version",
|
||||||
|
data: []byte("\xa1\x64kind\x61k"),
|
||||||
|
into: &unstructured.Unstructured{},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Kind: "k"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if !runtime.IsMissingVersion(err) {
|
||||||
|
t.Errorf("expected MissingVersion, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "into unstructured",
|
||||||
|
data: []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
|
||||||
|
into: &unstructured.Unstructured{},
|
||||||
|
expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v",
|
||||||
|
"kind": "k",
|
||||||
|
}},
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "using unstructured creater",
|
||||||
|
data: []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
|
||||||
|
metaFactory: &defaultMetaFactory{},
|
||||||
|
creater: stubCreater{obj: &unstructured.Unstructured{}},
|
||||||
|
expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v",
|
||||||
|
"kind": "k",
|
||||||
|
}},
|
||||||
|
expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
|
||||||
|
assertOnError: func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error, got: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
s := newSerializer(tc.metaFactory, tc.creater, tc.typer, tc.options...)
|
||||||
|
|
||||||
|
actualObj, actualGVK, err := s.Decode(tc.data, tc.gvk, tc.into)
|
||||||
|
tc.assertOnError(t, err)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tc.expectedObj, actualObj) {
|
||||||
|
t.Error(cmp.Diff(tc.expectedObj, actualObj))
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(tc.expectedGVK, actualGVK); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMetaFactoryInterpret(t *testing.T) {
|
||||||
|
mf := &defaultMetaFactory{}
|
||||||
|
_, err := mf.Interpret(nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected non-nil error")
|
||||||
|
}
|
||||||
|
gvk, err := mf.Interpret([]byte("\xa2\x6aapiVersion\x63a/b\x64kind\x61c"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(&schema.GroupVersionKind{Group: "a", Version: "b", Kind: "c"}, gvk); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubTyper struct {
|
||||||
|
gvks []schema.GroupVersionKind
|
||||||
|
unversioned bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t stubTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
|
||||||
|
return t.gvks, t.unversioned, t.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stubTyper) Recognizes(schema.GroupVersionKind) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubCreater struct {
|
||||||
|
obj runtime.Object
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c stubCreater) New(gvk schema.GroupVersionKind) (runtime.Object, error) {
|
||||||
|
return c.obj, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
type notRegisteredTyper struct{}
|
||||||
|
|
||||||
|
func (notRegisteredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
|
||||||
|
return nil, false, runtime.NewNotRegisteredErrForType("test", reflect.TypeOf(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (notRegisteredTyper) Recognizes(schema.GroupVersionKind) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type stubMetaFactory struct {
|
||||||
|
gvk *schema.GroupVersionKind
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mf stubMetaFactory) Interpret([]byte) (*schema.GroupVersionKind, error) {
|
||||||
|
return mf.gvk, mf.err
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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 provides functions for marshaling and unmarshaling between arbitrary Go values and
|
||||||
|
// CBOR data, with behavior that is compatible with that of the CBOR serializer. In particular,
|
||||||
|
// types that implement cbor.Marshaler and cbor.Unmarshaler should use these functions.
|
||||||
|
package direct
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Marshal(src interface{}) ([]byte, error) {
|
||||||
|
return modes.Encode.Marshal(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unmarshal(src []byte, dst interface{}) error {
|
||||||
|
return modes.Decode.Unmarshal(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Diagnose(src []byte) (string, error) {
|
||||||
|
return modes.Diagnostic.Diagnose(src)
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
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 modes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Decode cbor.DecMode = func() cbor.DecMode {
|
||||||
|
decode, err := cbor.DecOptions{
|
||||||
|
// Maps with duplicate keys are well-formed but invalid according to the CBOR spec
|
||||||
|
// and never acceptable. Unlike the JSON serializer, inputs containing duplicate map
|
||||||
|
// keys are rejected outright and not surfaced as a strict decoding error.
|
||||||
|
DupMapKey: cbor.DupMapKeyEnforcedAPF,
|
||||||
|
|
||||||
|
// For JSON parity, decoding an RFC3339 string into time.Time needs to be accepted
|
||||||
|
// with or without tagging. If a tag number is present, it must be valid.
|
||||||
|
TimeTag: cbor.DecTagOptional,
|
||||||
|
|
||||||
|
// Observed depth up to 16 in fuzzed batch/v1 CronJobList. JSON implementation limit
|
||||||
|
// is 10000.
|
||||||
|
MaxNestedLevels: 64,
|
||||||
|
|
||||||
|
MaxArrayElements: 1024,
|
||||||
|
MaxMapPairs: 1024,
|
||||||
|
|
||||||
|
// Indefinite-length sequences aren't produced by this serializer, but other
|
||||||
|
// implementations can.
|
||||||
|
IndefLength: cbor.IndefLengthAllowed,
|
||||||
|
|
||||||
|
// Accept inputs that contain CBOR tags.
|
||||||
|
TagsMd: cbor.TagsAllowed,
|
||||||
|
|
||||||
|
// Decode type 0 (unsigned integer) as int64.
|
||||||
|
// TODO: IntDecConvertSignedOrFail errors on overflow, JSON will try to fall back to float64.
|
||||||
|
IntDec: cbor.IntDecConvertSignedOrFail,
|
||||||
|
|
||||||
|
// Disable producing map[cbor.ByteString]interface{}, which is not acceptable for
|
||||||
|
// decodes into interface{}.
|
||||||
|
MapKeyByteString: cbor.MapKeyByteStringForbidden,
|
||||||
|
|
||||||
|
// Error on map keys that don't map to a field in the destination struct.
|
||||||
|
ExtraReturnErrors: cbor.ExtraDecErrorUnknownField,
|
||||||
|
|
||||||
|
// Decode maps into concrete type map[string]interface{} when the destination is an
|
||||||
|
// interface{}.
|
||||||
|
DefaultMapType: reflect.TypeOf(map[string]interface{}(nil)),
|
||||||
|
|
||||||
|
// A CBOR text string whose content is not a valid UTF-8 sequence is well-formed but
|
||||||
|
// invalid according to the CBOR spec. Reject invalid inputs. Encoders are
|
||||||
|
// responsible for ensuring that all text strings they produce contain valid UTF-8
|
||||||
|
// sequences and may use the byte string major type to encode strings that have not
|
||||||
|
// been validated.
|
||||||
|
UTF8: cbor.UTF8RejectInvalid,
|
||||||
|
|
||||||
|
// Never make a case-insensitive match between a map key and a struct field.
|
||||||
|
FieldNameMatching: cbor.FieldNameMatchingCaseSensitive,
|
||||||
|
|
||||||
|
// Produce string concrete values when decoding a CBOR byte string into interface{}.
|
||||||
|
DefaultByteStringType: reflect.TypeOf(""),
|
||||||
|
|
||||||
|
// Allow CBOR byte strings to be decoded into string destination values.
|
||||||
|
ByteStringToString: cbor.ByteStringToStringAllowed,
|
||||||
|
|
||||||
|
// Allow CBOR byte strings to match struct fields when appearing as a map key.
|
||||||
|
FieldNameByteString: cbor.FieldNameByteStringAllowed,
|
||||||
|
|
||||||
|
// When decoding an unrecognized tag to interface{}, return the decoded tag content
|
||||||
|
// instead of the default, a cbor.Tag representing a (number, content) pair.
|
||||||
|
UnrecognizedTagToAny: cbor.UnrecognizedTagContentToAny,
|
||||||
|
}.DecMode()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return decode
|
||||||
|
}()
|
||||||
|
|
||||||
|
// DecodeLax is derived from Decode, but does not complain about unknown fields in the input.
|
||||||
|
var DecodeLax cbor.DecMode = func() cbor.DecMode {
|
||||||
|
opts := Decode.DecOptions()
|
||||||
|
opts.ExtraReturnErrors &^= cbor.ExtraDecErrorUnknownField // clear bit
|
||||||
|
dm, err := opts.DecMode()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return dm
|
||||||
|
}()
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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 modes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Diagnostic cbor.DiagMode = func() cbor.DiagMode {
|
||||||
|
opts := Decode.DecOptions()
|
||||||
|
diagnostic, err := cbor.DiagOptions{
|
||||||
|
ByteStringText: true,
|
||||||
|
|
||||||
|
MaxNestedLevels: opts.MaxNestedLevels,
|
||||||
|
MaxArrayElements: opts.MaxArrayElements,
|
||||||
|
MaxMapPairs: opts.MaxMapPairs,
|
||||||
|
}.DiagMode()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return diagnostic
|
||||||
|
}()
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
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 modes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Encode cbor.EncMode = func() cbor.EncMode {
|
||||||
|
encode, err := cbor.EncOptions{
|
||||||
|
// Map keys need to be sorted to have deterministic output, and this is the order
|
||||||
|
// defined in RFC 8949 4.2.1 "Core Deterministic Encoding Requirements".
|
||||||
|
Sort: cbor.SortBytewiseLexical,
|
||||||
|
|
||||||
|
// CBOR supports distinct types for IEEE-754 float16, float32, and float64. Store
|
||||||
|
// floats in the smallest width that preserves value so that equivalent float32 and
|
||||||
|
// float64 values encode to identical bytes, as they do in a JSON
|
||||||
|
// encoding. Satisfies one of the "Core Deterministic Encoding Requirements".
|
||||||
|
ShortestFloat: cbor.ShortestFloat16,
|
||||||
|
|
||||||
|
// ShortestFloat doesn't apply to NaN or Inf values. Inf values are losslessly
|
||||||
|
// encoded to float16. RFC 8949 recommends choosing a single representation of NaN
|
||||||
|
// in applications that do not smuggle additional information inside NaN values, we
|
||||||
|
// use 0x7e00.
|
||||||
|
NaNConvert: cbor.NaNConvert7e00,
|
||||||
|
InfConvert: cbor.InfConvertFloat16,
|
||||||
|
|
||||||
|
// Prefer encoding math/big.Int to one of the 64-bit integer types if it fits. When
|
||||||
|
// later decoded into Unstructured, the set of allowable concrete numeric types is
|
||||||
|
// limited to int64 and float64, so the distinction between big integer and integer
|
||||||
|
// can't be preserved.
|
||||||
|
BigIntConvert: cbor.BigIntConvertShortest,
|
||||||
|
|
||||||
|
// MarshalJSON for time.Time writes RFC3339 with nanos.
|
||||||
|
Time: cbor.TimeRFC3339Nano,
|
||||||
|
|
||||||
|
// The decoder must be able to accept RFC3339 strings with or without tag 0 (e.g. by
|
||||||
|
// the end of time.Time -> JSON -> Unstructured -> CBOR, the CBOR encoder has no
|
||||||
|
// reliable way of knowing that a particular string originated from serializing a
|
||||||
|
// time.Time), so producing tag 0 has little use.
|
||||||
|
TimeTag: cbor.EncTagNone,
|
||||||
|
|
||||||
|
// Indefinite-length items have multiple encodings and aren't being used anyway, so
|
||||||
|
// disable to avoid an opportunity for nondeterminism.
|
||||||
|
IndefLength: cbor.IndefLengthForbidden,
|
||||||
|
|
||||||
|
// Preserve distinction between nil and empty for slices and maps.
|
||||||
|
NilContainers: cbor.NilContainerAsNull,
|
||||||
|
|
||||||
|
// OK to produce tags.
|
||||||
|
TagsMd: cbor.TagsAllowed,
|
||||||
|
|
||||||
|
// Use the same definition of "empty" as encoding/json.
|
||||||
|
OmitEmpty: cbor.OmitEmptyGoValue,
|
||||||
|
|
||||||
|
// The CBOR types text string and byte string are structurally equivalent, with the
|
||||||
|
// semantic difference that a text string whose content is an invalid UTF-8 sequence
|
||||||
|
// is itself invalid. We reject all invalid text strings at decode time and do not
|
||||||
|
// validate or sanitize all Go strings at encode time. Encoding Go strings to the
|
||||||
|
// byte string type is comparable to the existing Protobuf behavior and cheaply
|
||||||
|
// ensures that the output is valid CBOR.
|
||||||
|
String: cbor.StringToByteString,
|
||||||
|
|
||||||
|
// Encode struct field names to the byte string type rather than the text string
|
||||||
|
// type.
|
||||||
|
FieldName: cbor.FieldNameToByteString,
|
||||||
|
}.EncMode()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return encode
|
||||||
|
}()
|
||||||
|
|
||||||
|
var EncodeNondeterministic cbor.EncMode = func() cbor.EncMode {
|
||||||
|
opts := Encode.EncOptions()
|
||||||
|
opts.Sort = cbor.SortNone
|
||||||
|
em, err := opts.EncMode()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return em
|
||||||
|
}()
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1313,6 +1313,7 @@ k8s.io/apimachinery/pkg/runtime
|
|||||||
k8s.io/apimachinery/pkg/runtime/schema
|
k8s.io/apimachinery/pkg/runtime/schema
|
||||||
k8s.io/apimachinery/pkg/runtime/serializer
|
k8s.io/apimachinery/pkg/runtime/serializer
|
||||||
k8s.io/apimachinery/pkg/runtime/serializer/cbor
|
k8s.io/apimachinery/pkg/runtime/serializer/cbor
|
||||||
|
k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes
|
||||||
k8s.io/apimachinery/pkg/runtime/serializer/json
|
k8s.io/apimachinery/pkg/runtime/serializer/json
|
||||||
k8s.io/apimachinery/pkg/runtime/serializer/protobuf
|
k8s.io/apimachinery/pkg/runtime/serializer/protobuf
|
||||||
k8s.io/apimachinery/pkg/runtime/serializer/recognizer
|
k8s.io/apimachinery/pkg/runtime/serializer/recognizer
|
||||||
|
Loading…
Reference in New Issue
Block a user