123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- /*
- 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"
- "encoding/json"
- "fmt"
- "io"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "k8s.io/apimachinery/pkg/conversion/queryparams"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/klog/v2"
- )
- // 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) {
- 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)
- }
- // 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{}
- const noopEncoderIdentifier Identifier = "noop"
- func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
- // There is no need to handle runtime.CacheableObject, as we don't
- // process the obj at all.
- return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
- }
- // Identifier implements runtime.Encoder interface.
- func (n NoopEncoder) Identifier() Identifier {
- return noopEncoderIdentifier
- }
- // 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
- identifier Identifier
- }
- func NewBase64Serializer(e Encoder, d Decoder) Serializer {
- return &base64Serializer{
- Encoder: e,
- Decoder: d,
- identifier: identifier(e),
- }
- }
- func identifier(e Encoder) Identifier {
- result := map[string]string{
- "name": "base64",
- }
- if e != nil {
- result["encoder"] = string(e.Identifier())
- }
- identifier, err := json.Marshal(result)
- if err != nil {
- klog.Fatalf("Failed marshaling identifier for base64Serializer: %v", err)
- }
- return Identifier(identifier)
- }
- func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
- if co, ok := obj.(CacheableObject); ok {
- return co.CacheEncode(s.Identifier(), s.doEncode, stream)
- }
- return s.doEncode(obj, stream)
- }
- func (s base64Serializer) doEncode(obj Object, stream io.Writer) error {
- e := base64.NewEncoder(base64.StdEncoding, stream)
- err := s.Encoder.Encode(obj, e)
- e.Close()
- return err
- }
- // Identifier implements runtime.Encoder interface.
- func (s base64Serializer) Identifier() Identifier {
- return s.identifier
- }
- 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{}
- )
- const (
- internalGroupVersionerIdentifier = "internal"
- disabledGroupVersionerIdentifier = "disabled"
- )
- 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
- }
- // Identifier implements GroupVersioner interface.
- func (internalGroupVersioner) Identifier() string {
- return internalGroupVersionerIdentifier
- }
- type disabledGroupVersioner struct{}
- // KindForGroupVersionKinds returns false for any input.
- func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
- return schema.GroupVersionKind{}, false
- }
- // Identifier implements GroupVersioner interface.
- func (disabledGroupVersioner) Identifier() string {
- return disabledGroupVersionerIdentifier
- }
- // 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
- coerce bool
- }
- // 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}
- }
- // NewCoercingMultiGroupVersioner returns the provided group version for any incoming kind.
- // Incoming kinds that match the provided groupKinds are preferred.
- // Kind may be empty in the provided group kind, in which case any kind will match.
- // Examples:
- //
- // gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar
- // KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind)
- //
- // gv=mygroup/__internal, groupKinds=mygroup, anothergroup
- // KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group)
- //
- // gv=mygroup/__internal, groupKinds=mygroup, anothergroup
- // KindForGroupVersionKinds(yetanother/v1/Baz, yetanother/v1/Bar) -> mygroup/__internal/Baz (no preferred group/kind match, uses first kind in list)
- func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
- return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true}
- }
- // 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
- }
- }
- if v.coerce && len(kinds) > 0 {
- return v.target.WithKind(kinds[0].Kind), true
- }
- return schema.GroupVersionKind{}, false
- }
- // Identifier implements GroupVersioner interface.
- func (v multiGroupVersioner) Identifier() string {
- groupKinds := make([]string, 0, len(v.acceptedGroupKinds))
- for _, gk := range v.acceptedGroupKinds {
- groupKinds = append(groupKinds, gk.String())
- }
- result := map[string]string{
- "name": "multi",
- "target": v.target.String(),
- "accepted": strings.Join(groupKinds, ","),
- "coerce": strconv.FormatBool(v.coerce),
- }
- identifier, err := json.Marshal(result)
- if err != nil {
- klog.Fatalf("Failed marshaling Identifier for %#v: %v", v, err)
- }
- return string(identifier)
- }
|