...

Source file src/k8s.io/apimachinery/pkg/runtime/codec.go

Documentation: k8s.io/apimachinery/pkg/runtime

     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 runtime
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"net/url"
    26  	"reflect"
    27  	"strconv"
    28  	"strings"
    29  
    30  	"k8s.io/apimachinery/pkg/conversion/queryparams"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	"k8s.io/klog/v2"
    33  )
    34  
    35  // codec binds an encoder and decoder.
    36  type codec struct {
    37  	Encoder
    38  	Decoder
    39  }
    40  
    41  // NewCodec creates a Codec from an Encoder and Decoder.
    42  func NewCodec(e Encoder, d Decoder) Codec {
    43  	return codec{e, d}
    44  }
    45  
    46  // Encode is a convenience wrapper for encoding to a []byte from an Encoder
    47  func Encode(e Encoder, obj Object) ([]byte, error) {
    48  	buf := &bytes.Buffer{}
    49  	if err := e.Encode(obj, buf); err != nil {
    50  		return nil, err
    51  	}
    52  	return buf.Bytes(), nil
    53  }
    54  
    55  // Decode is a convenience wrapper for decoding data into an Object.
    56  func Decode(d Decoder, data []byte) (Object, error) {
    57  	obj, _, err := d.Decode(data, nil, nil)
    58  	return obj, err
    59  }
    60  
    61  // DecodeInto performs a Decode into the provided object.
    62  func DecodeInto(d Decoder, data []byte, into Object) error {
    63  	out, gvk, err := d.Decode(data, nil, into)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	if out != into {
    68  		return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into))
    69  	}
    70  	return nil
    71  }
    72  
    73  // EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
    74  func EncodeOrDie(e Encoder, obj Object) string {
    75  	bytes, err := Encode(e, obj)
    76  	if err != nil {
    77  		panic(err)
    78  	}
    79  	return string(bytes)
    80  }
    81  
    82  // UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
    83  // invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
    84  func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk schema.GroupVersionKind, obj Object) (Object, error) {
    85  	if obj != nil {
    86  		kinds, _, err := t.ObjectKinds(obj)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		for _, kind := range kinds {
    91  			if gvk == kind {
    92  				return obj, nil
    93  			}
    94  		}
    95  	}
    96  	return c.New(gvk)
    97  }
    98  
    99  // NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
   100  type NoopEncoder struct {
   101  	Decoder
   102  }
   103  
   104  var _ Serializer = NoopEncoder{}
   105  
   106  const noopEncoderIdentifier Identifier = "noop"
   107  
   108  func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
   109  	// There is no need to handle runtime.CacheableObject, as we don't
   110  	// process the obj at all.
   111  	return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
   112  }
   113  
   114  // Identifier implements runtime.Encoder interface.
   115  func (n NoopEncoder) Identifier() Identifier {
   116  	return noopEncoderIdentifier
   117  }
   118  
   119  // NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
   120  type NoopDecoder struct {
   121  	Encoder
   122  }
   123  
   124  var _ Serializer = NoopDecoder{}
   125  
   126  func (n NoopDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
   127  	return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder))
   128  }
   129  
   130  // NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
   131  func NewParameterCodec(scheme *Scheme) ParameterCodec {
   132  	return &parameterCodec{
   133  		typer:     scheme,
   134  		convertor: scheme,
   135  		creator:   scheme,
   136  		defaulter: scheme,
   137  	}
   138  }
   139  
   140  // parameterCodec implements conversion to and from query parameters and objects.
   141  type parameterCodec struct {
   142  	typer     ObjectTyper
   143  	convertor ObjectConvertor
   144  	creator   ObjectCreater
   145  	defaulter ObjectDefaulter
   146  }
   147  
   148  var _ ParameterCodec = &parameterCodec{}
   149  
   150  // DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then
   151  // converts that object to into (if necessary). Returns an error if the operation cannot be completed.
   152  func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into Object) error {
   153  	if len(parameters) == 0 {
   154  		return nil
   155  	}
   156  	targetGVKs, _, err := c.typer.ObjectKinds(into)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	for i := range targetGVKs {
   161  		if targetGVKs[i].GroupVersion() == from {
   162  			if err := c.convertor.Convert(&parameters, into, nil); err != nil {
   163  				return err
   164  			}
   165  			// in the case where we going into the same object we're receiving, default on the outbound object
   166  			if c.defaulter != nil {
   167  				c.defaulter.Default(into)
   168  			}
   169  			return nil
   170  		}
   171  	}
   172  
   173  	input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
   174  	if err != nil {
   175  		return err
   176  	}
   177  	if err := c.convertor.Convert(&parameters, input, nil); err != nil {
   178  		return err
   179  	}
   180  	// if we have defaulter, default the input before converting to output
   181  	if c.defaulter != nil {
   182  		c.defaulter.Default(input)
   183  	}
   184  	return c.convertor.Convert(input, into, nil)
   185  }
   186  
   187  // EncodeParameters converts the provided object into the to version, then converts that object to url.Values.
   188  // Returns an error if conversion is not possible.
   189  func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (url.Values, error) {
   190  	gvks, _, err := c.typer.ObjectKinds(obj)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	gvk := gvks[0]
   195  	if to != gvk.GroupVersion() {
   196  		out, err := c.convertor.ConvertToVersion(obj, to)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		obj = out
   201  	}
   202  	return queryparams.Convert(obj)
   203  }
   204  
   205  type base64Serializer struct {
   206  	Encoder
   207  	Decoder
   208  
   209  	identifier Identifier
   210  }
   211  
   212  func NewBase64Serializer(e Encoder, d Decoder) Serializer {
   213  	return &base64Serializer{
   214  		Encoder:    e,
   215  		Decoder:    d,
   216  		identifier: identifier(e),
   217  	}
   218  }
   219  
   220  func identifier(e Encoder) Identifier {
   221  	result := map[string]string{
   222  		"name": "base64",
   223  	}
   224  	if e != nil {
   225  		result["encoder"] = string(e.Identifier())
   226  	}
   227  	identifier, err := json.Marshal(result)
   228  	if err != nil {
   229  		klog.Fatalf("Failed marshaling identifier for base64Serializer: %v", err)
   230  	}
   231  	return Identifier(identifier)
   232  }
   233  
   234  func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
   235  	if co, ok := obj.(CacheableObject); ok {
   236  		return co.CacheEncode(s.Identifier(), s.doEncode, stream)
   237  	}
   238  	return s.doEncode(obj, stream)
   239  }
   240  
   241  func (s base64Serializer) doEncode(obj Object, stream io.Writer) error {
   242  	e := base64.NewEncoder(base64.StdEncoding, stream)
   243  	err := s.Encoder.Encode(obj, e)
   244  	e.Close()
   245  	return err
   246  }
   247  
   248  // Identifier implements runtime.Encoder interface.
   249  func (s base64Serializer) Identifier() Identifier {
   250  	return s.identifier
   251  }
   252  
   253  func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
   254  	out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
   255  	n, err := base64.StdEncoding.Decode(out, data)
   256  	if err != nil {
   257  		return nil, nil, err
   258  	}
   259  	return s.Decoder.Decode(out[:n], defaults, into)
   260  }
   261  
   262  // SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot
   263  // include media-type parameters), or the first info with an empty media type, or false if no type matches.
   264  func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
   265  	for _, info := range types {
   266  		if info.MediaType == mediaType {
   267  			return info, true
   268  		}
   269  	}
   270  	for _, info := range types {
   271  		if len(info.MediaType) == 0 {
   272  			return info, true
   273  		}
   274  	}
   275  	return SerializerInfo{}, false
   276  }
   277  
   278  var (
   279  	// InternalGroupVersioner will always prefer the internal version for a given group version kind.
   280  	InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
   281  	// DisabledGroupVersioner will reject all kinds passed to it.
   282  	DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
   283  )
   284  
   285  const (
   286  	internalGroupVersionerIdentifier = "internal"
   287  	disabledGroupVersionerIdentifier = "disabled"
   288  )
   289  
   290  type internalGroupVersioner struct{}
   291  
   292  // KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
   293  func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
   294  	for _, kind := range kinds {
   295  		if kind.Version == APIVersionInternal {
   296  			return kind, true
   297  		}
   298  	}
   299  	for _, kind := range kinds {
   300  		return schema.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
   301  	}
   302  	return schema.GroupVersionKind{}, false
   303  }
   304  
   305  // Identifier implements GroupVersioner interface.
   306  func (internalGroupVersioner) Identifier() string {
   307  	return internalGroupVersionerIdentifier
   308  }
   309  
   310  type disabledGroupVersioner struct{}
   311  
   312  // KindForGroupVersionKinds returns false for any input.
   313  func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
   314  	return schema.GroupVersionKind{}, false
   315  }
   316  
   317  // Identifier implements GroupVersioner interface.
   318  func (disabledGroupVersioner) Identifier() string {
   319  	return disabledGroupVersionerIdentifier
   320  }
   321  
   322  // Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
   323  var _ GroupVersioner = schema.GroupVersion{}
   324  var _ GroupVersioner = schema.GroupVersions{}
   325  var _ GroupVersioner = multiGroupVersioner{}
   326  
   327  type multiGroupVersioner struct {
   328  	target             schema.GroupVersion
   329  	acceptedGroupKinds []schema.GroupKind
   330  	coerce             bool
   331  }
   332  
   333  // NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
   334  // Kind may be empty in the provided group kind, in which case any kind will match.
   335  func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
   336  	if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
   337  		return gv
   338  	}
   339  	return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
   340  }
   341  
   342  // NewCoercingMultiGroupVersioner returns the provided group version for any incoming kind.
   343  // Incoming kinds that match the provided groupKinds are preferred.
   344  // Kind may be empty in the provided group kind, in which case any kind will match.
   345  // Examples:
   346  //
   347  //	gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar
   348  //	KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind)
   349  //
   350  //	gv=mygroup/__internal, groupKinds=mygroup, anothergroup
   351  //	KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group)
   352  //
   353  //	gv=mygroup/__internal, groupKinds=mygroup, anothergroup
   354  //	KindForGroupVersionKinds(yetanother/v1/Baz, yetanother/v1/Bar) -> mygroup/__internal/Baz (no preferred group/kind match, uses first kind in list)
   355  func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
   356  	return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true}
   357  }
   358  
   359  // KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
   360  // use the originating kind where possible.
   361  func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
   362  	for _, src := range kinds {
   363  		for _, kind := range v.acceptedGroupKinds {
   364  			if kind.Group != src.Group {
   365  				continue
   366  			}
   367  			if len(kind.Kind) > 0 && kind.Kind != src.Kind {
   368  				continue
   369  			}
   370  			return v.target.WithKind(src.Kind), true
   371  		}
   372  	}
   373  	if v.coerce && len(kinds) > 0 {
   374  		return v.target.WithKind(kinds[0].Kind), true
   375  	}
   376  	return schema.GroupVersionKind{}, false
   377  }
   378  
   379  // Identifier implements GroupVersioner interface.
   380  func (v multiGroupVersioner) Identifier() string {
   381  	groupKinds := make([]string, 0, len(v.acceptedGroupKinds))
   382  	for _, gk := range v.acceptedGroupKinds {
   383  		groupKinds = append(groupKinds, gk.String())
   384  	}
   385  	result := map[string]string{
   386  		"name":     "multi",
   387  		"target":   v.target.String(),
   388  		"accepted": strings.Join(groupKinds, ","),
   389  		"coerce":   strconv.FormatBool(v.coerce),
   390  	}
   391  	identifier, err := json.Marshal(result)
   392  	if err != nil {
   393  		klog.Fatalf("Failed marshaling Identifier for %#v: %v", v, err)
   394  	}
   395  	return string(identifier)
   396  }
   397  

View as plain text