rebase: update K8s packages to v0.32.1

Update K8s packages in go.mod to v0.32.1

Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
Praveen M
2025-01-16 09:41:46 +05:30
committed by mergify[bot]
parent 5aef21ea4e
commit 7eb99fc6c9
2442 changed files with 273386 additions and 47788 deletions

View File

@ -284,3 +284,21 @@ func (e *encoderWithAllocator) Encode(obj Object, w io.Writer) error {
func (e *encoderWithAllocator) Identifier() Identifier {
return e.encoder.Identifier()
}
type nondeterministicEncoderToEncoderAdapter struct {
NondeterministicEncoder
}
func (e nondeterministicEncoderToEncoderAdapter) Encode(obj Object, w io.Writer) error {
return e.EncodeNondeterministic(obj, w)
}
// UseNondeterministicEncoding returns an Encoder that encodes objects using the provided Encoder's
// EncodeNondeterministic method if it implements NondeterministicEncoder, otherwise it returns the
// provided Encoder as-is.
func UseNondeterministicEncoding(encoder Encoder) Encoder {
if nondeterministic, ok := encoder.(NondeterministicEncoder); ok {
return nondeterministicEncoderToEncoderAdapter{nondeterministic}
}
return encoder
}

View File

@ -69,6 +69,19 @@ type Encoder interface {
Identifier() Identifier
}
// NondeterministicEncoder is implemented by Encoders that can serialize objects more efficiently in
// cases where the output does not need to be deterministic.
type NondeterministicEncoder interface {
Encoder
// EncodeNondeterministic writes an object to the stream. Unlike the Encode method of
// Encoder, EncodeNondeterministic does not guarantee that any two invocations will write
// the same sequence of bytes to the io.Writer. Any differences will not be significant to a
// generic decoder. For example, map entries and struct fields might be encoded in any
// order.
EncodeNondeterministic(Object, io.Writer) error
}
// MemoryAllocator is responsible for allocating memory.
// By encapsulating memory allocation into its own interface, we can reuse the memory
// across many operations in places we know it can significantly improve the performance.

View File

@ -0,0 +1,389 @@
/*
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 cbor
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
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"
"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
runtime.NondeterministicEncoder
recognizer.RecognizingDecoder
// NewSerializer returns a value of this interface type rather than exporting the serializer
// type and returning one of those because the zero value of serializer isn't ready to
// use. Users aren't intended to implement cbor.Serializer themselves, and this unexported
// interface method is here to prevent that (https://go.dev/blog/module-compatibility).
private()
}
var _ Serializer = &serializer{}
type options struct {
strict bool
transcode bool
}
type Option func(*options)
// Strict configures a serializer to return a strict decoding error when it encounters map keys that
// do not correspond to a field in the target object of a decode operation. This option is disabled
// by default.
func Strict(s bool) Option {
return func(opts *options) {
opts.strict = s
}
}
// Transcode configures a serializer to transcode the "raw" bytes of a decoded runtime.RawExtension
// or metav1.FieldsV1 object to JSON. This is enabled by default to support existing programs that
// depend on the assumption that objects of either type contain valid JSON.
func Transcode(s bool) Option {
return func(opts *options) {
opts.transcode = s
}
}
type serializer struct {
metaFactory metaFactory
creater runtime.ObjectCreater
typer runtime.ObjectTyper
options options
}
func (serializer) private() {}
// NewSerializer creates and returns a serializer configured with the provided options. The default
// options are equivalent to explicitly passing Strict(false) and Transcode(true).
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,
}
s.options.transcode = true
for _, o := range options {
o(&s.options)
}
return s
}
func (s *serializer) Identifier() runtime.Identifier {
return "cbor"
}
// Encode writes a CBOR representation of the given object.
//
// Because the CBOR data item written by a call to Encode is always enclosed in the "self-described
// CBOR" tag, its encoded form always has the prefix 0xd9d9f7. This prefix is suitable for use as a
// "magic number" for distinguishing encoded CBOR from other protocols.
//
// The default serialization behavior for any given object replicates the behavior of the JSON
// serializer as far as it is necessary to allow the CBOR serializer to be used as a drop-in
// replacement for the JSON serializer, with limited exceptions. For example, the distinction
// between integers and floating-point numbers is preserved in CBOR due to its distinct
// representations for each type.
//
// Objects implementing runtime.Unstructured will have their unstructured content encoded rather
// than following the default behavior for their dynamic type.
func (s *serializer) Encode(obj runtime.Object, w io.Writer) error {
return s.encode(modes.Encode, obj, w)
}
func (s *serializer) EncodeNondeterministic(obj runtime.Object, w io.Writer) error {
return s.encode(modes.EncodeNondeterministic, obj, w)
}
func (s *serializer) encode(mode modes.EncMode, obj runtime.Object, w io.Writer) error {
var v interface{} = obj
if u, ok := obj.(runtime.Unstructured); ok {
v = u.UnstructuredContent()
}
if err := modes.RejectCustomMarshalers(v); err != nil {
return err
}
if _, err := w.Write(selfDescribedCBOR); err != nil {
return err
}
return mode.MarshalTo(v, w)
}
// 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
}
// unmarshal unmarshals CBOR data from the provided byte slice into a Go object. If the decoder is
// configured to report strict errors, the first error return value may be a non-nil strict decoding
// error. If the last error return value is non-nil, then the unmarshal failed entirely and the
// state of the destination object should not be relied on.
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() {
switch u := u.(type) {
case *unstructured.UnstructuredList:
// UnstructuredList's implementation of SetUnstructuredContent
// produces different objects than those produced by a decode using
// UnstructuredJSONScheme:
//
// 1. SetUnstructuredContent retains the "items" key in the list's
// Object field. It is omitted from Object when decoding with
// UnstructuredJSONScheme.
// 2. SetUnstructuredContent does not populate "apiVersion" and
// "kind" on each entry of its Items
// field. UnstructuredJSONScheme does, inferring the singular
// Kind from the list Kind.
// 3. SetUnstructuredContent ignores entries of "items" that are
// not JSON objects or are objects without
// "kind". UnstructuredJSONScheme returns an error in either
// case.
//
// UnstructuredJSONScheme's behavior is replicated here.
var items []interface{}
if uncast, present := content["items"]; present {
var cast bool
items, cast = uncast.([]interface{})
if !cast {
strict, lax = nil, fmt.Errorf("items field of UnstructuredList must be encoded as an array or null if present")
return
}
}
apiVersion, _ := content["apiVersion"].(string)
kind, _ := content["kind"].(string)
kind = strings.TrimSuffix(kind, "List")
var unstructureds []unstructured.Unstructured
if len(items) > 0 {
unstructureds = make([]unstructured.Unstructured, len(items))
}
for i := range items {
object, cast := items[i].(map[string]interface{})
if !cast {
strict, lax = nil, fmt.Errorf("elements of the items field of UnstructuredList must be encoded as a map")
return
}
// As in UnstructuredJSONScheme, only set the heuristic
// singular GVK when both "apiVersion" and "kind" are either
// missing, non-string, or empty.
object["apiVersion"], _ = object["apiVersion"].(string)
object["kind"], _ = object["kind"].(string)
if object["apiVersion"] == "" && object["kind"] == "" {
object["apiVersion"] = apiVersion
object["kind"] = kind
}
if object["kind"] == "" {
strict, lax = nil, runtime.NewMissingKindErr(diagnose(data))
return
}
if object["apiVersion"] == "" {
strict, lax = nil, runtime.NewMissingVersionErr(diagnose(data))
return
}
unstructureds[i].Object = object
}
delete(content, "items")
u.Object = content
u.Items = unstructureds
default:
u.SetUnstructuredContent(content)
}
}()
into = &content
} else if err := modes.RejectCustomMarshalers(into); err != nil {
return nil, err
}
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
}
if s.options.transcode {
if err := transcodeRawTypes(obj); 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
}
// NewSerializerInfo returns a default SerializerInfo for CBOR using the given creater and typer.
func NewSerializerInfo(creater runtime.ObjectCreater, typer runtime.ObjectTyper) runtime.SerializerInfo {
return runtime.SerializerInfo{
MediaType: "application/cbor",
MediaTypeType: "application",
MediaTypeSubType: "cbor",
Serializer: NewSerializer(creater, typer),
StrictSerializer: NewSerializer(creater, typer, Strict(true)),
StreamSerializer: &runtime.StreamSerializerInfo{
Framer: NewFramer(),
Serializer: NewSerializer(creater, typer, Transcode(false)),
},
}
}

View File

@ -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)
}

View File

@ -0,0 +1,90 @@
/*
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 cbor
import (
"io"
"k8s.io/apimachinery/pkg/runtime"
"github.com/fxamacker/cbor/v2"
)
// NewFramer returns a runtime.Framer based on RFC 8742 CBOR Sequences. Each frame contains exactly
// one encoded CBOR data item.
func NewFramer() runtime.Framer {
return framer{}
}
var _ runtime.Framer = framer{}
type framer struct{}
func (framer) NewFrameReader(rc io.ReadCloser) io.ReadCloser {
return &frameReader{
decoder: cbor.NewDecoder(rc),
closer: rc,
}
}
func (framer) NewFrameWriter(w io.Writer) io.Writer {
// Each data item in a CBOR sequence is self-delimiting (like JSON objects).
return w
}
type frameReader struct {
decoder *cbor.Decoder
closer io.Closer
overflow []byte
}
func (fr *frameReader) Read(dst []byte) (int, error) {
if len(fr.overflow) > 0 {
// We read a frame that was too large for the destination slice in a previous call
// to Read and have bytes left over.
n := copy(dst, fr.overflow)
if n < len(fr.overflow) {
fr.overflow = fr.overflow[n:]
return n, io.ErrShortBuffer
}
fr.overflow = nil
return n, nil
}
// The Reader contract allows implementations to use all of dst[0:len(dst)] as scratch
// space, even if n < len(dst), but it does not allow implementations to use
// dst[len(dst):cap(dst)]. Slicing it up-front allows us to append to it without worrying
// about overwriting dst[len(dst):cap(dst)].
m := cbor.RawMessage(dst[0:0:len(dst)])
if err := fr.decoder.Decode(&m); err != nil {
return 0, err
}
if len(m) > len(dst) {
// The frame was too big, m has a newly-allocated underlying array to accommodate
// it.
fr.overflow = m[len(dst):]
return copy(dst, m), io.ErrShortBuffer
}
return len(m), nil
}
func (fr *frameReader) Close() error {
return fr.closer.Close()
}

View File

@ -105,7 +105,7 @@ var Encode = EncMode{
var EncodeNondeterministic = EncMode{
delegate: func() cbor.UserBufferEncMode {
opts := Encode.options()
opts.Sort = cbor.SortNone // TODO: Use cbor.SortFastShuffle after bump to v2.7.0.
opts.Sort = cbor.SortFastShuffle
em, err := opts.UserBufferEncMode()
if err != nil {
panic(err)

View File

@ -0,0 +1,236 @@
/*
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 cbor
import (
"fmt"
"reflect"
"sync"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
var sharedTranscoders transcoders
var rawTypeTranscodeFuncs = map[reflect.Type]func(reflect.Value) error{
reflect.TypeFor[runtime.RawExtension](): func(rv reflect.Value) error {
if !rv.CanAddr() {
return nil
}
re := rv.Addr().Interface().(*runtime.RawExtension)
if re.Raw == nil {
// When Raw is nil it encodes to null. Don't change nil Raw values during
// transcoding, they would have unmarshalled from JSON as nil too.
return nil
}
j, err := re.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to transcode RawExtension to JSON: %w", err)
}
re.Raw = j
return nil
},
reflect.TypeFor[metav1.FieldsV1](): func(rv reflect.Value) error {
if !rv.CanAddr() {
return nil
}
fields := rv.Addr().Interface().(*metav1.FieldsV1)
if fields.Raw == nil {
// When Raw is nil it encodes to null. Don't change nil Raw values during
// transcoding, they would have unmarshalled from JSON as nil too.
return nil
}
j, err := fields.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to transcode FieldsV1 to JSON: %w", err)
}
fields.Raw = j
return nil
},
}
func transcodeRawTypes(v interface{}) error {
if v == nil {
return nil
}
rv := reflect.ValueOf(v)
return sharedTranscoders.getTranscoder(rv.Type()).fn(rv)
}
type transcoder struct {
fn func(rv reflect.Value) error
}
var noop = transcoder{
fn: func(reflect.Value) error {
return nil
},
}
type transcoders struct {
lock sync.RWMutex
m map[reflect.Type]**transcoder
}
func (ts *transcoders) getTranscoder(rt reflect.Type) transcoder {
ts.lock.RLock()
tpp, ok := ts.m[rt]
ts.lock.RUnlock()
if ok {
return **tpp
}
ts.lock.Lock()
defer ts.lock.Unlock()
tp := ts.getTranscoderLocked(rt)
return *tp
}
func (ts *transcoders) getTranscoderLocked(rt reflect.Type) *transcoder {
if tpp, ok := ts.m[rt]; ok {
// A transcoder for this type was cached while waiting to acquire the lock.
return *tpp
}
// Cache the transcoder now, before populating fn, so that circular references between types
// don't overflow the call stack.
t := new(transcoder)
if ts.m == nil {
ts.m = make(map[reflect.Type]**transcoder)
}
ts.m[rt] = &t
for rawType, fn := range rawTypeTranscodeFuncs {
if rt == rawType {
t = &transcoder{fn: fn}
return t
}
}
switch rt.Kind() {
case reflect.Array:
te := ts.getTranscoderLocked(rt.Elem())
rtlen := rt.Len()
if rtlen == 0 || te == &noop {
t = &noop
break
}
t.fn = func(rv reflect.Value) error {
for i := 0; i < rtlen; i++ {
if err := te.fn(rv.Index(i)); err != nil {
return err
}
}
return nil
}
case reflect.Interface:
// Any interface value might have a dynamic type involving RawExtension. It needs to
// be checked.
t.fn = func(rv reflect.Value) error {
if rv.IsNil() {
return nil
}
rv = rv.Elem()
// The interface element's type is dynamic so its transcoder can't be
// determined statically.
return ts.getTranscoder(rv.Type()).fn(rv)
}
case reflect.Map:
rtk := rt.Key()
tk := ts.getTranscoderLocked(rtk)
rte := rt.Elem()
te := ts.getTranscoderLocked(rte)
if tk == &noop && te == &noop {
t = &noop
break
}
t.fn = func(rv reflect.Value) error {
iter := rv.MapRange()
rvk := reflect.New(rtk).Elem()
rve := reflect.New(rte).Elem()
for iter.Next() {
rvk.SetIterKey(iter)
if err := tk.fn(rvk); err != nil {
return err
}
rve.SetIterValue(iter)
if err := te.fn(rve); err != nil {
return err
}
}
return nil
}
case reflect.Pointer:
te := ts.getTranscoderLocked(rt.Elem())
if te == &noop {
t = &noop
break
}
t.fn = func(rv reflect.Value) error {
if rv.IsNil() {
return nil
}
return te.fn(rv.Elem())
}
case reflect.Slice:
te := ts.getTranscoderLocked(rt.Elem())
if te == &noop {
t = &noop
break
}
t.fn = func(rv reflect.Value) error {
for i := 0; i < rv.Len(); i++ {
if err := te.fn(rv.Index(i)); err != nil {
return err
}
}
return nil
}
case reflect.Struct:
type fieldTranscoder struct {
Index int
Transcoder *transcoder
}
var fieldTranscoders []fieldTranscoder
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
tf := ts.getTranscoderLocked(f.Type)
if tf == &noop {
continue
}
fieldTranscoders = append(fieldTranscoders, fieldTranscoder{Index: i, Transcoder: tf})
}
if len(fieldTranscoders) == 0 {
t = &noop
break
}
t.fn = func(rv reflect.Value) error {
for _, ft := range fieldTranscoders {
if err := ft.Transcoder.fn(rv.Field(ft.Index)); err != nil {
return err
}
}
return nil
}
default:
t = &noop
}
return t
}

View File

@ -17,9 +17,6 @@ limitations under the License.
package serializer
import (
"mime"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
@ -28,41 +25,26 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
)
// serializerExtensions are for serializers that are conditionally compiled in
var serializerExtensions = []func(*runtime.Scheme) (serializerType, bool){}
type serializerType struct {
AcceptContentTypes []string
ContentType string
FileExtensions []string
// EncodesAsText should be true if this content type can be represented safely in UTF-8
EncodesAsText bool
Serializer runtime.Serializer
PrettySerializer runtime.Serializer
StrictSerializer runtime.Serializer
AcceptStreamContentTypes []string
StreamContentType string
Framer runtime.Framer
StreamSerializer runtime.Serializer
}
func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, options CodecFactoryOptions) []serializerType {
func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, options CodecFactoryOptions) []runtime.SerializerInfo {
jsonSerializer := json.NewSerializerWithOptions(
mf, scheme, scheme,
json.SerializerOptions{Yaml: false, Pretty: false, Strict: options.Strict},
)
jsonSerializerType := serializerType{
AcceptContentTypes: []string{runtime.ContentTypeJSON},
ContentType: runtime.ContentTypeJSON,
FileExtensions: []string{"json"},
EncodesAsText: true,
Serializer: jsonSerializer,
Framer: json.Framer,
StreamSerializer: jsonSerializer,
jsonSerializerType := runtime.SerializerInfo{
MediaType: runtime.ContentTypeJSON,
MediaTypeType: "application",
MediaTypeSubType: "json",
EncodesAsText: true,
Serializer: jsonSerializer,
StrictSerializer: json.NewSerializerWithOptions(
mf, scheme, scheme,
json.SerializerOptions{Yaml: false, Pretty: false, Strict: true},
),
StreamSerializer: &runtime.StreamSerializerInfo{
EncodesAsText: true,
Serializer: jsonSerializer,
Framer: json.Framer,
},
}
if options.Pretty {
jsonSerializerType.PrettySerializer = json.NewSerializerWithOptions(
@ -71,12 +53,6 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, option
)
}
strictJSONSerializer := json.NewSerializerWithOptions(
mf, scheme, scheme,
json.SerializerOptions{Yaml: false, Pretty: false, Strict: true},
)
jsonSerializerType.StrictSerializer = strictJSONSerializer
yamlSerializer := json.NewSerializerWithOptions(
mf, scheme, scheme,
json.SerializerOptions{Yaml: true, Pretty: false, Strict: options.Strict},
@ -88,35 +64,35 @@ func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, option
protoSerializer := protobuf.NewSerializer(scheme, scheme)
protoRawSerializer := protobuf.NewRawSerializer(scheme, scheme)
serializers := []serializerType{
serializers := []runtime.SerializerInfo{
jsonSerializerType,
{
AcceptContentTypes: []string{runtime.ContentTypeYAML},
ContentType: runtime.ContentTypeYAML,
FileExtensions: []string{"yaml"},
EncodesAsText: true,
Serializer: yamlSerializer,
StrictSerializer: strictYAMLSerializer,
MediaType: runtime.ContentTypeYAML,
MediaTypeType: "application",
MediaTypeSubType: "yaml",
EncodesAsText: true,
Serializer: yamlSerializer,
StrictSerializer: strictYAMLSerializer,
},
{
AcceptContentTypes: []string{runtime.ContentTypeProtobuf},
ContentType: runtime.ContentTypeProtobuf,
FileExtensions: []string{"pb"},
Serializer: protoSerializer,
MediaType: runtime.ContentTypeProtobuf,
MediaTypeType: "application",
MediaTypeSubType: "vnd.kubernetes.protobuf",
Serializer: protoSerializer,
// note, strict decoding is unsupported for protobuf,
// fall back to regular serializing
StrictSerializer: protoSerializer,
Framer: protobuf.LengthDelimitedFramer,
StreamSerializer: protoRawSerializer,
StreamSerializer: &runtime.StreamSerializerInfo{
Serializer: protoRawSerializer,
Framer: protobuf.LengthDelimitedFramer,
},
},
}
for _, fn := range serializerExtensions {
if serializer, ok := fn(scheme); ok {
serializers = append(serializers, serializer)
}
for _, f := range options.serializers {
serializers = append(serializers, f(scheme, scheme))
}
return serializers
}
@ -136,6 +112,8 @@ type CodecFactoryOptions struct {
Strict bool
// Pretty includes a pretty serializer along with the non-pretty one
Pretty bool
serializers []func(runtime.ObjectCreater, runtime.ObjectTyper) runtime.SerializerInfo
}
// CodecFactoryOptionsMutator takes a pointer to an options struct and then modifies it.
@ -162,6 +140,13 @@ func DisableStrict(options *CodecFactoryOptions) {
options.Strict = false
}
// WithSerializer configures a serializer to be supported in addition to the default serializers.
func WithSerializer(f func(runtime.ObjectCreater, runtime.ObjectTyper) runtime.SerializerInfo) CodecFactoryOptionsMutator {
return func(options *CodecFactoryOptions) {
options.serializers = append(options.serializers, f)
}
}
// NewCodecFactory provides methods for retrieving serializers for the supported wire formats
// and conversion wrappers to define preferred internal and external versions. In the future,
// as the internal version is used less, callers may instead use a defaulting serializer and
@ -184,7 +169,7 @@ func NewCodecFactory(scheme *runtime.Scheme, mutators ...CodecFactoryOptionsMuta
}
// newCodecFactory is a helper for testing that allows a different metafactory to be specified.
func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) CodecFactory {
func newCodecFactory(scheme *runtime.Scheme, serializers []runtime.SerializerInfo) CodecFactory {
decoders := make([]runtime.Decoder, 0, len(serializers))
var accepts []runtime.SerializerInfo
alreadyAccepted := make(map[string]struct{})
@ -192,38 +177,20 @@ func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) Codec
var legacySerializer runtime.Serializer
for _, d := range serializers {
decoders = append(decoders, d.Serializer)
for _, mediaType := range d.AcceptContentTypes {
if _, ok := alreadyAccepted[mediaType]; ok {
continue
}
alreadyAccepted[mediaType] = struct{}{}
info := runtime.SerializerInfo{
MediaType: d.ContentType,
EncodesAsText: d.EncodesAsText,
Serializer: d.Serializer,
PrettySerializer: d.PrettySerializer,
StrictSerializer: d.StrictSerializer,
}
if _, ok := alreadyAccepted[d.MediaType]; ok {
continue
}
alreadyAccepted[d.MediaType] = struct{}{}
mediaType, _, err := mime.ParseMediaType(info.MediaType)
if err != nil {
panic(err)
}
parts := strings.SplitN(mediaType, "/", 2)
info.MediaTypeType = parts[0]
info.MediaTypeSubType = parts[1]
acceptedSerializerShallowCopy := d
if d.StreamSerializer != nil {
cloned := *d.StreamSerializer
acceptedSerializerShallowCopy.StreamSerializer = &cloned
}
accepts = append(accepts, acceptedSerializerShallowCopy)
if d.StreamSerializer != nil {
info.StreamSerializer = &runtime.StreamSerializerInfo{
Serializer: d.StreamSerializer,
EncodesAsText: d.EncodesAsText,
Framer: d.Framer,
}
}
accepts = append(accepts, info)
if mediaType == runtime.ContentTypeJSON {
legacySerializer = d.Serializer
}
if d.MediaType == runtime.ContentTypeJSON {
legacySerializer = d.Serializer
}
}
if legacySerializer == nil {

View File

@ -43,10 +43,11 @@ type TypeMeta struct {
}
const (
ContentTypeJSON string = "application/json"
ContentTypeYAML string = "application/yaml"
ContentTypeProtobuf string = "application/vnd.kubernetes.protobuf"
ContentTypeCBOR string = "application/cbor"
ContentTypeJSON string = "application/json"
ContentTypeYAML string = "application/yaml"
ContentTypeProtobuf string = "application/vnd.kubernetes.protobuf"
ContentTypeCBOR string = "application/cbor" // RFC 8949
ContentTypeCBORSequence string = "application/cbor-seq" // RFC 8742
)
// RawExtension is used to hold extensions in external versions.