123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- Copyright 2014 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 runtime
- import (
- "bytes"
- "encoding/base64"
- "fmt"
- "io"
- "net/url"
- "reflect"
- "k8s.io/apimachinery/pkg/conversion/queryparams"
- "k8s.io/apimachinery/pkg/runtime/schema"
- )
- // codec binds an encoder and decoder.
- type codec struct {
- Encoder
- Decoder
- }
- // NewCodec creates a Codec from an Encoder and Decoder.
- func NewCodec(e Encoder, d Decoder) Codec {
- return codec{e, d}
- }
- // Encode is a convenience wrapper for encoding to a []byte from an Encoder
- func Encode(e Encoder, obj Object) ([]byte, error) {
- // TODO: reuse buffer
- buf := &bytes.Buffer{}
- if err := e.Encode(obj, buf); err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
- }
- // Decode is a convenience wrapper for decoding data into an Object.
- func Decode(d Decoder, data []byte) (Object, error) {
- obj, _, err := d.Decode(data, nil, nil)
- return obj, err
- }
- // DecodeInto performs a Decode into the provided object.
- func DecodeInto(d Decoder, data []byte, into Object) error {
- out, gvk, err := d.Decode(data, nil, into)
- if err != nil {
- return err
- }
- if out != into {
- return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into))
- }
- return nil
- }
- // EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
- func EncodeOrDie(e Encoder, obj Object) string {
- bytes, err := Encode(e, obj)
- if err != nil {
- panic(err)
- }
- return string(bytes)
- }
- // DefaultingSerializer invokes defaulting after decoding.
- type DefaultingSerializer struct {
- Defaulter ObjectDefaulter
- Decoder Decoder
- // Encoder is optional to allow this type to be used as both a Decoder and an Encoder
- Encoder
- }
- // Decode performs a decode and then allows the defaulter to act on the provided object.
- func (d DefaultingSerializer) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
- obj, gvk, err := d.Decoder.Decode(data, defaultGVK, into)
- if err != nil {
- return obj, gvk, err
- }
- d.Defaulter.Default(obj)
- return obj, gvk, nil
- }
- // UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
- // invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
- func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk schema.GroupVersionKind, obj Object) (Object, error) {
- if obj != nil {
- kinds, _, err := t.ObjectKinds(obj)
- if err != nil {
- return nil, err
- }
- for _, kind := range kinds {
- if gvk == kind {
- return obj, nil
- }
- }
- }
- return c.New(gvk)
- }
- // NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
- type NoopEncoder struct {
- Decoder
- }
- var _ Serializer = NoopEncoder{}
- func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
- return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
- }
- // NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
- type NoopDecoder struct {
- Encoder
- }
- var _ Serializer = NoopDecoder{}
- func (n NoopDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
- return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder))
- }
- // NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
- func NewParameterCodec(scheme *Scheme) ParameterCodec {
- return ¶meterCodec{
- typer: scheme,
- convertor: scheme,
- creator: scheme,
- defaulter: scheme,
- }
- }
- // parameterCodec implements conversion to and from query parameters and objects.
- type parameterCodec struct {
- typer ObjectTyper
- convertor ObjectConvertor
- creator ObjectCreater
- defaulter ObjectDefaulter
- }
- var _ ParameterCodec = ¶meterCodec{}
- // DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then
- // converts that object to into (if necessary). Returns an error if the operation cannot be completed.
- func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into Object) error {
- if len(parameters) == 0 {
- return nil
- }
- targetGVKs, _, err := c.typer.ObjectKinds(into)
- if err != nil {
- return err
- }
- for i := range targetGVKs {
- if targetGVKs[i].GroupVersion() == from {
- if err := c.convertor.Convert(¶meters, into, nil); err != nil {
- return err
- }
- // in the case where we going into the same object we're receiving, default on the outbound object
- if c.defaulter != nil {
- c.defaulter.Default(into)
- }
- return nil
- }
- }
- input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
- if err != nil {
- return err
- }
- if err := c.convertor.Convert(¶meters, input, nil); err != nil {
- return err
- }
- // if we have defaulter, default the input before converting to output
- if c.defaulter != nil {
- c.defaulter.Default(input)
- }
- return c.convertor.Convert(input, into, nil)
- }
- // EncodeParameters converts the provided object into the to version, then converts that object to url.Values.
- // Returns an error if conversion is not possible.
- func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (url.Values, error) {
- gvks, _, err := c.typer.ObjectKinds(obj)
- if err != nil {
- return nil, err
- }
- gvk := gvks[0]
- if to != gvk.GroupVersion() {
- out, err := c.convertor.ConvertToVersion(obj, to)
- if err != nil {
- return nil, err
- }
- obj = out
- }
- return queryparams.Convert(obj)
- }
- type base64Serializer struct {
- Encoder
- Decoder
- }
- func NewBase64Serializer(e Encoder, d Decoder) Serializer {
- return &base64Serializer{e, d}
- }
- func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
- e := base64.NewEncoder(base64.StdEncoding, stream)
- err := s.Encoder.Encode(obj, e)
- e.Close()
- return err
- }
- func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
- out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
- n, err := base64.StdEncoding.Decode(out, data)
- if err != nil {
- return nil, nil, err
- }
- return s.Decoder.Decode(out[:n], defaults, into)
- }
- // SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot
- // include media-type parameters), or the first info with an empty media type, or false if no type matches.
- func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
- for _, info := range types {
- if info.MediaType == mediaType {
- return info, true
- }
- }
- for _, info := range types {
- if len(info.MediaType) == 0 {
- return info, true
- }
- }
- return SerializerInfo{}, false
- }
- var (
- // InternalGroupVersioner will always prefer the internal version for a given group version kind.
- InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
- // DisabledGroupVersioner will reject all kinds passed to it.
- DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
- )
- type internalGroupVersioner struct{}
- // KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
- func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
- for _, kind := range kinds {
- if kind.Version == APIVersionInternal {
- return kind, true
- }
- }
- for _, kind := range kinds {
- return schema.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
- }
- return schema.GroupVersionKind{}, false
- }
- type disabledGroupVersioner struct{}
- // KindForGroupVersionKinds returns false for any input.
- func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
- return schema.GroupVersionKind{}, false
- }
- // GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
- type GroupVersioners []GroupVersioner
- // KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occurred.
- func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
- for _, gv := range gvs {
- target, ok := gv.KindForGroupVersionKinds(kinds)
- if !ok {
- continue
- }
- return target, true
- }
- return schema.GroupVersionKind{}, false
- }
- // Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
- var _ GroupVersioner = schema.GroupVersion{}
- var _ GroupVersioner = schema.GroupVersions{}
- var _ GroupVersioner = multiGroupVersioner{}
- type multiGroupVersioner struct {
- target schema.GroupVersion
- acceptedGroupKinds []schema.GroupKind
- }
- // NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
- // Kind may be empty in the provided group kind, in which case any kind will match.
- func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
- if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
- return gv
- }
- return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
- }
- // KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
- // use the originating kind where possible.
- func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
- for _, src := range kinds {
- for _, kind := range v.acceptedGroupKinds {
- if kind.Group != src.Group {
- continue
- }
- if len(kind.Kind) > 0 && kind.Kind != src.Kind {
- continue
- }
- return v.target.WithKind(src.Kind), true
- }
- }
- return schema.GroupVersionKind{}, false
- }
|