github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/versioning/versioning.go (about)

     1  /*
     2  Copyright 2014 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 versioning
    18  
    19  import (
    20  	"encoding/json"
    21  	"io"
    22  	"reflect"
    23  	"sync"
    24  
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    28  	"k8s.io/klog/v2"
    29  )
    30  
    31  // NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
    32  func NewDefaultingCodecForScheme(
    33  // TODO: I should be a scheme interface?
    34  	scheme *runtime.Scheme,
    35  	encoder runtime.Encoder,
    36  	decoder runtime.Decoder,
    37  	encodeVersion runtime.GroupVersioner,
    38  	decodeVersion runtime.GroupVersioner,
    39  ) runtime.Codec {
    40  	return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion, scheme.Name())
    41  }
    42  
    43  // NewCodec takes objects in their internal versions and converts them to external versions before
    44  // serializing them. It assumes the serializer provided to it only deals with external versions.
    45  // This class is also a serializer, but is generally used with a specific version.
    46  func NewCodec(
    47  	encoder runtime.Encoder,
    48  	decoder runtime.Decoder,
    49  	convertor runtime.ObjectConvertor,
    50  	creater runtime.ObjectCreater,
    51  	typer runtime.ObjectTyper,
    52  	defaulter runtime.ObjectDefaulter,
    53  	encodeVersion runtime.GroupVersioner,
    54  	decodeVersion runtime.GroupVersioner,
    55  	originalSchemeName string,
    56  ) runtime.Codec {
    57  	internal := &codec{
    58  		encoder:   encoder,
    59  		decoder:   decoder,
    60  		convertor: convertor,
    61  		creater:   creater,
    62  		typer:     typer,
    63  		defaulter: defaulter,
    64  
    65  		encodeVersion: encodeVersion,
    66  		decodeVersion: decodeVersion,
    67  
    68  		identifier: identifier(encodeVersion, encoder),
    69  
    70  		originalSchemeName: originalSchemeName,
    71  	}
    72  	return internal
    73  }
    74  
    75  type codec struct {
    76  	encoder   runtime.Encoder
    77  	decoder   runtime.Decoder
    78  	convertor runtime.ObjectConvertor
    79  	creater   runtime.ObjectCreater
    80  	typer     runtime.ObjectTyper
    81  	defaulter runtime.ObjectDefaulter
    82  
    83  	encodeVersion runtime.GroupVersioner
    84  	decodeVersion runtime.GroupVersioner
    85  
    86  	identifier runtime.Identifier
    87  
    88  	// originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates
    89  	originalSchemeName string
    90  }
    91  
    92  var _ runtime.EncoderWithAllocator = &codec{}
    93  
    94  var identifiersMap sync.Map
    95  
    96  type codecIdentifier struct {
    97  	EncodeGV string `json:"encodeGV,omitempty"`
    98  	Encoder  string `json:"encoder,omitempty"`
    99  	Name     string `json:"name,omitempty"`
   100  }
   101  
   102  // identifier computes Identifier of Encoder based on codec parameters.
   103  func identifier(encodeGV runtime.GroupVersioner, encoder runtime.Encoder) runtime.Identifier {
   104  	result := codecIdentifier{
   105  		Name: "versioning",
   106  	}
   107  
   108  	if encodeGV != nil {
   109  		result.EncodeGV = encodeGV.Identifier()
   110  	}
   111  	if encoder != nil {
   112  		result.Encoder = string(encoder.Identifier())
   113  	}
   114  	if id, ok := identifiersMap.Load(result); ok {
   115  		return id.(runtime.Identifier)
   116  	}
   117  	identifier, err := json.Marshal(result)
   118  	if err != nil {
   119  		klog.Fatalf("Failed marshaling identifier for codec: %v", err)
   120  	}
   121  	identifiersMap.Store(result, runtime.Identifier(identifier))
   122  	return runtime.Identifier(identifier)
   123  }
   124  
   125  // Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
   126  // successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
   127  // into that matches the serialized version.
   128  func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
   129  	// If the into object is unstructured and expresses an opinion about its group/version,
   130  	// create a new instance of the type so we always exercise the conversion path (skips short-circuiting on `into == obj`)
   131  	decodeInto := into
   132  	if into != nil {
   133  		if _, ok := into.(runtime.Unstructured); ok && !into.GetObjectKind().GroupVersionKind().GroupVersion().Empty() {
   134  			decodeInto = reflect.New(reflect.TypeOf(into).Elem()).Interface().(runtime.Object)
   135  		}
   136  	}
   137  
   138  	var strictDecodingErrs []error
   139  	obj, gvk, err := c.decoder.Decode(data, defaultGVK, decodeInto)
   140  	if err != nil {
   141  		if strictErr, ok := runtime.AsStrictDecodingError(err); obj != nil && ok {
   142  			// save the strictDecodingError and let the caller decide what to do with it
   143  			strictDecodingErrs = append(strictDecodingErrs, strictErr.Errors()...)
   144  		} else {
   145  			return nil, gvk, err
   146  		}
   147  	}
   148  
   149  	if d, ok := obj.(runtime.NestedObjectDecoder); ok {
   150  		if err := d.DecodeNestedObjects(runtime.WithoutVersionDecoder{c.decoder}); err != nil {
   151  			if strictErr, ok := runtime.AsStrictDecodingError(err); ok {
   152  				// save the strictDecodingError let and the caller decide what to do with it
   153  				strictDecodingErrs = append(strictDecodingErrs, strictErr.Errors()...)
   154  			} else {
   155  				return nil, gvk, err
   156  
   157  			}
   158  		}
   159  	}
   160  
   161  	// aggregate the strict decoding errors into one
   162  	var strictDecodingErr error
   163  	if len(strictDecodingErrs) > 0 {
   164  		strictDecodingErr = runtime.NewStrictDecodingError(strictDecodingErrs)
   165  	}
   166  	// if we specify a target, use generic conversion.
   167  	if into != nil {
   168  		// perform defaulting if requested
   169  		if c.defaulter != nil {
   170  			c.defaulter.Default(obj)
   171  		}
   172  
   173  		// Short-circuit conversion if the into object is same object
   174  		if into == obj {
   175  			return into, gvk, strictDecodingErr
   176  		}
   177  
   178  		if err := c.convertor.Convert(obj, into, c.decodeVersion); err != nil {
   179  			return nil, gvk, err
   180  		}
   181  
   182  		return into, gvk, strictDecodingErr
   183  	}
   184  
   185  	// perform defaulting if requested
   186  	if c.defaulter != nil {
   187  		c.defaulter.Default(obj)
   188  	}
   189  
   190  	out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
   191  	if err != nil {
   192  		return nil, gvk, err
   193  	}
   194  	return out, gvk, strictDecodingErr
   195  }
   196  
   197  // EncodeWithAllocator ensures the provided object is output in the appropriate group and version, invoking
   198  // conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
   199  // In addition, it allows for providing a memory allocator for efficient memory usage during object serialization.
   200  func (c *codec) EncodeWithAllocator(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
   201  	return c.encode(obj, w, memAlloc)
   202  }
   203  
   204  // Encode ensures the provided object is output in the appropriate group and version, invoking
   205  // conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
   206  func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
   207  	return c.encode(obj, w, nil)
   208  }
   209  
   210  func (c *codec) encode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
   211  	if co, ok := obj.(runtime.CacheableObject); ok {
   212  		return co.CacheEncode(c.Identifier(), func(obj runtime.Object, w io.Writer) error { return c.doEncode(obj, w, memAlloc) }, w)
   213  	}
   214  	return c.doEncode(obj, w, memAlloc)
   215  }
   216  
   217  func (c *codec) doEncode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
   218  	encodeFn := c.encoder.Encode
   219  	if memAlloc != nil {
   220  		if encoder, supportsAllocator := c.encoder.(runtime.EncoderWithAllocator); supportsAllocator {
   221  			encodeFn = func(obj runtime.Object, w io.Writer) error {
   222  				return encoder.EncodeWithAllocator(obj, w, memAlloc)
   223  			}
   224  		} else {
   225  			klog.V(6).Infof("a memory allocator was provided but the encoder %s doesn't implement the runtime.EncoderWithAllocator, using regular encoder.Encode method", c.encoder.Identifier())
   226  		}
   227  	}
   228  	switch obj := obj.(type) {
   229  	case *runtime.Unknown:
   230  		return encodeFn(obj, w)
   231  	case runtime.Unstructured:
   232  		// An unstructured list can contain objects of multiple group version kinds. don't short-circuit just
   233  		// because the top-level type matches our desired destination type. actually send the object to the converter
   234  		// to give it a chance to convert the list items if needed.
   235  		if _, ok := obj.(*unstructured.UnstructuredList); !ok {
   236  			// avoid conversion roundtrip if GVK is the right one already or is empty (yes, this is a hack, but the old behaviour we rely on in kubectl)
   237  			objGVK := obj.GetObjectKind().GroupVersionKind()
   238  			if len(objGVK.Version) == 0 {
   239  				return encodeFn(obj, w)
   240  			}
   241  			targetGVK, ok := c.encodeVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{objGVK})
   242  			if !ok {
   243  				return runtime.NewNotRegisteredGVKErrForTarget(c.originalSchemeName, objGVK, c.encodeVersion)
   244  			}
   245  			if targetGVK == objGVK {
   246  				return encodeFn(obj, w)
   247  			}
   248  		}
   249  	}
   250  
   251  	gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	objectKind := obj.GetObjectKind()
   257  	old := objectKind.GroupVersionKind()
   258  	// restore the old GVK after encoding
   259  	defer objectKind.SetGroupVersionKind(old)
   260  
   261  	if c.encodeVersion == nil || isUnversioned {
   262  		if e, ok := obj.(runtime.NestedObjectEncoder); ok {
   263  			if err := e.EncodeNestedObjects(runtime.WithVersionEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
   264  				return err
   265  			}
   266  		}
   267  		objectKind.SetGroupVersionKind(gvks[0])
   268  		return encodeFn(obj, w)
   269  	}
   270  
   271  	// Perform a conversion if necessary
   272  	out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	if e, ok := out.(runtime.NestedObjectEncoder); ok {
   278  		if err := e.EncodeNestedObjects(runtime.WithVersionEncoder{Version: c.encodeVersion, Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
   279  			return err
   280  		}
   281  	}
   282  
   283  	// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
   284  	return encodeFn(out, w)
   285  }
   286  
   287  // Identifier implements runtime.Encoder interface.
   288  func (c *codec) Identifier() runtime.Identifier {
   289  	return c.identifier
   290  }