...

Source file src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/cbor.go

Documentation: k8s.io/apimachinery/pkg/runtime/serializer/cbor

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cbor
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
    30  	"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
    31  	util "k8s.io/apimachinery/pkg/util/runtime"
    32  
    33  	"github.com/fxamacker/cbor/v2"
    34  )
    35  
    36  type metaFactory interface {
    37  	// Interpret should return the version and kind of the wire-format of the object.
    38  	Interpret(data []byte) (*schema.GroupVersionKind, error)
    39  }
    40  
    41  type defaultMetaFactory struct{}
    42  
    43  func (mf *defaultMetaFactory) Interpret(data []byte) (*schema.GroupVersionKind, error) {
    44  	var tm metav1.TypeMeta
    45  	// The input is expected to include additional map keys besides apiVersion and kind, so use
    46  	// lax mode for decoding into TypeMeta.
    47  	if err := modes.DecodeLax.Unmarshal(data, &tm); err != nil {
    48  		return nil, fmt.Errorf("unable to determine group/version/kind: %w", err)
    49  	}
    50  	actual := tm.GetObjectKind().GroupVersionKind()
    51  	return &actual, nil
    52  }
    53  
    54  type Serializer interface {
    55  	runtime.Serializer
    56  	recognizer.RecognizingDecoder
    57  }
    58  
    59  var _ Serializer = &serializer{}
    60  
    61  type options struct {
    62  	strict bool
    63  }
    64  
    65  type Option func(*options)
    66  
    67  func Strict(s bool) Option {
    68  	return func(opts *options) {
    69  		opts.strict = s
    70  	}
    71  }
    72  
    73  type serializer struct {
    74  	metaFactory metaFactory
    75  	creater     runtime.ObjectCreater
    76  	typer       runtime.ObjectTyper
    77  	options     options
    78  }
    79  
    80  func NewSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) Serializer {
    81  	return newSerializer(&defaultMetaFactory{}, creater, typer, options...)
    82  }
    83  
    84  func newSerializer(metaFactory metaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) *serializer {
    85  	s := &serializer{
    86  		metaFactory: metaFactory,
    87  		creater:     creater,
    88  		typer:       typer,
    89  	}
    90  	for _, o := range options {
    91  		o(&s.options)
    92  	}
    93  	return s
    94  }
    95  
    96  func (s *serializer) Identifier() runtime.Identifier {
    97  	return "cbor"
    98  }
    99  
   100  func (s *serializer) Encode(obj runtime.Object, w io.Writer) error {
   101  	if _, err := w.Write(selfDescribedCBOR); err != nil {
   102  		return err
   103  	}
   104  
   105  	e := modes.Encode.NewEncoder(w)
   106  	if u, ok := obj.(runtime.Unstructured); ok {
   107  		return e.Encode(u.UnstructuredContent())
   108  	}
   109  	return e.Encode(obj)
   110  }
   111  
   112  // gvkWithDefaults returns group kind and version defaulting from provided default
   113  func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
   114  	if len(actual.Kind) == 0 {
   115  		actual.Kind = defaultGVK.Kind
   116  	}
   117  	if len(actual.Version) == 0 && len(actual.Group) == 0 {
   118  		actual.Group = defaultGVK.Group
   119  		actual.Version = defaultGVK.Version
   120  	}
   121  	if len(actual.Version) == 0 && actual.Group == defaultGVK.Group {
   122  		actual.Version = defaultGVK.Version
   123  	}
   124  	return actual
   125  }
   126  
   127  // diagnose returns the diagnostic encoding of a well-formed CBOR data item.
   128  func diagnose(data []byte) string {
   129  	diag, err := modes.Diagnostic.Diagnose(data)
   130  	if err != nil {
   131  		// Since the input must already be well-formed CBOR, converting it to diagnostic
   132  		// notation should not fail.
   133  		util.HandleError(err)
   134  
   135  		return hex.EncodeToString(data)
   136  	}
   137  	return diag
   138  }
   139  
   140  func (s *serializer) unmarshal(data []byte, into interface{}) (strict, lax error) {
   141  	if u, ok := into.(runtime.Unstructured); ok {
   142  		var content map[string]interface{}
   143  		defer func() {
   144  			// TODO: The UnstructuredList implementation of SetUnstructuredContent is
   145  			// not identical to what unstructuredJSONScheme does: (1) it retains the
   146  			// "items" key in its Object field, and (2) it does not infer a singular
   147  			// Kind from the list's Kind and populate omitted apiVersion/kind for all
   148  			// entries in Items.
   149  			u.SetUnstructuredContent(content)
   150  		}()
   151  		into = &content
   152  	}
   153  
   154  	if !s.options.strict {
   155  		return nil, modes.DecodeLax.Unmarshal(data, into)
   156  	}
   157  
   158  	err := modes.Decode.Unmarshal(data, into)
   159  	// TODO: UnknownFieldError is ambiguous. It only provides the index of the first problematic
   160  	// map entry encountered and does not indicate which map the index refers to.
   161  	var unknownField *cbor.UnknownFieldError
   162  	if errors.As(err, &unknownField) {
   163  		// Unlike JSON, there are no strict errors in CBOR for duplicate map keys. CBOR maps
   164  		// with duplicate keys are considered invalid according to the spec and are rejected
   165  		// entirely.
   166  		return runtime.NewStrictDecodingError([]error{unknownField}), modes.DecodeLax.Unmarshal(data, into)
   167  	}
   168  	return nil, err
   169  }
   170  
   171  func (s *serializer) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
   172  	// A preliminary pass over the input to obtain the actual GVK is redundant on a successful
   173  	// decode into Unstructured.
   174  	if _, ok := into.(runtime.Unstructured); ok {
   175  		if _, unmarshalErr := s.unmarshal(data, into); unmarshalErr != nil {
   176  			actual, interpretErr := s.metaFactory.Interpret(data)
   177  			if interpretErr != nil {
   178  				return nil, nil, interpretErr
   179  			}
   180  
   181  			if gvk != nil {
   182  				*actual = gvkWithDefaults(*actual, *gvk)
   183  			}
   184  
   185  			return nil, actual, unmarshalErr
   186  		}
   187  
   188  		actual := into.GetObjectKind().GroupVersionKind()
   189  		if len(actual.Kind) == 0 {
   190  			return nil, &actual, runtime.NewMissingKindErr(diagnose(data))
   191  		}
   192  		if len(actual.Version) == 0 {
   193  			return nil, &actual, runtime.NewMissingVersionErr(diagnose(data))
   194  		}
   195  
   196  		return into, &actual, nil
   197  	}
   198  
   199  	actual, err := s.metaFactory.Interpret(data)
   200  	if err != nil {
   201  		return nil, nil, err
   202  	}
   203  
   204  	if gvk != nil {
   205  		*actual = gvkWithDefaults(*actual, *gvk)
   206  	}
   207  
   208  	if into != nil {
   209  		types, _, err := s.typer.ObjectKinds(into)
   210  		if err != nil {
   211  			return nil, actual, err
   212  		}
   213  		*actual = gvkWithDefaults(*actual, types[0])
   214  	}
   215  
   216  	if len(actual.Kind) == 0 {
   217  		return nil, actual, runtime.NewMissingKindErr(diagnose(data))
   218  	}
   219  	if len(actual.Version) == 0 {
   220  		return nil, actual, runtime.NewMissingVersionErr(diagnose(data))
   221  	}
   222  
   223  	obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
   224  	if err != nil {
   225  		return nil, actual, err
   226  	}
   227  
   228  	strict, err := s.unmarshal(data, obj)
   229  	if err != nil {
   230  		return nil, actual, err
   231  	}
   232  	return obj, actual, strict
   233  }
   234  
   235  // selfDescribedCBOR is the CBOR encoding of the head of tag number 55799. This tag, specified in
   236  // RFC 8949 Section 3.4.6 "Self-Described CBOR", encloses all output from the encoder, has no
   237  // special semantics, and is used as a magic number to recognize CBOR-encoded data items.
   238  //
   239  // See https://www.rfc-editor.org/rfc/rfc8949.html#name-self-described-cbor.
   240  var selfDescribedCBOR = []byte{0xd9, 0xd9, 0xf7}
   241  
   242  func (s *serializer) RecognizesData(data []byte) (ok, unknown bool, err error) {
   243  	return bytes.HasPrefix(data, selfDescribedCBOR), false, nil
   244  }
   245  

View as plain text