2 Copyright 2014 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
27 "k8s.io/apimachinery/pkg/conversion/queryparams"
28 "k8s.io/apimachinery/pkg/runtime/schema"
31 // codec binds an encoder and decoder.
37 // NewCodec creates a Codec from an Encoder and Decoder.
38 func NewCodec(e Encoder, d Decoder) Codec {
42 // Encode is a convenience wrapper for encoding to a []byte from an Encoder
43 func Encode(e Encoder, obj Object) ([]byte, error) {
45 buf := &bytes.Buffer{}
46 if err := e.Encode(obj, buf); err != nil {
49 return buf.Bytes(), nil
52 // Decode is a convenience wrapper for decoding data into an Object.
53 func Decode(d Decoder, data []byte) (Object, error) {
54 obj, _, err := d.Decode(data, nil, nil)
58 // DecodeInto performs a Decode into the provided object.
59 func DecodeInto(d Decoder, data []byte, into Object) error {
60 out, gvk, err := d.Decode(data, nil, into)
65 return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into))
70 // EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
71 func EncodeOrDie(e Encoder, obj Object) string {
72 bytes, err := Encode(e, obj)
79 // UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
80 // invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
81 func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk schema.GroupVersionKind, obj Object) (Object, error) {
83 kinds, _, err := t.ObjectKinds(obj)
87 for _, kind := range kinds {
96 // NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
97 type NoopEncoder struct {
101 var _ Serializer = NoopEncoder{}
103 func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
104 return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
107 // NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
108 type NoopDecoder struct {
112 var _ Serializer = NoopDecoder{}
114 func (n NoopDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
115 return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder))
118 // NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
119 func NewParameterCodec(scheme *Scheme) ParameterCodec {
120 return ¶meterCodec{
128 // parameterCodec implements conversion to and from query parameters and objects.
129 type parameterCodec struct {
131 convertor ObjectConvertor
132 creator ObjectCreater
133 defaulter ObjectDefaulter
136 var _ ParameterCodec = ¶meterCodec{}
138 // DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then
139 // converts that object to into (if necessary). Returns an error if the operation cannot be completed.
140 func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into Object) error {
141 if len(parameters) == 0 {
144 targetGVKs, _, err := c.typer.ObjectKinds(into)
148 for i := range targetGVKs {
149 if targetGVKs[i].GroupVersion() == from {
150 if err := c.convertor.Convert(¶meters, into, nil); err != nil {
153 // in the case where we going into the same object we're receiving, default on the outbound object
154 if c.defaulter != nil {
155 c.defaulter.Default(into)
161 input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
165 if err := c.convertor.Convert(¶meters, input, nil); err != nil {
168 // if we have defaulter, default the input before converting to output
169 if c.defaulter != nil {
170 c.defaulter.Default(input)
172 return c.convertor.Convert(input, into, nil)
175 // EncodeParameters converts the provided object into the to version, then converts that object to url.Values.
176 // Returns an error if conversion is not possible.
177 func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (url.Values, error) {
178 gvks, _, err := c.typer.ObjectKinds(obj)
183 if to != gvk.GroupVersion() {
184 out, err := c.convertor.ConvertToVersion(obj, to)
190 return queryparams.Convert(obj)
193 type base64Serializer struct {
198 func NewBase64Serializer(e Encoder, d Decoder) Serializer {
199 return &base64Serializer{e, d}
202 func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
203 e := base64.NewEncoder(base64.StdEncoding, stream)
204 err := s.Encoder.Encode(obj, e)
209 func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
210 out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
211 n, err := base64.StdEncoding.Decode(out, data)
215 return s.Decoder.Decode(out[:n], defaults, into)
218 // SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot
219 // include media-type parameters), or the first info with an empty media type, or false if no type matches.
220 func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
221 for _, info := range types {
222 if info.MediaType == mediaType {
226 for _, info := range types {
227 if len(info.MediaType) == 0 {
231 return SerializerInfo{}, false
235 // InternalGroupVersioner will always prefer the internal version for a given group version kind.
236 InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
237 // DisabledGroupVersioner will reject all kinds passed to it.
238 DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
241 type internalGroupVersioner struct{}
243 // KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
244 func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
245 for _, kind := range kinds {
246 if kind.Version == APIVersionInternal {
250 for _, kind := range kinds {
251 return schema.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
253 return schema.GroupVersionKind{}, false
256 type disabledGroupVersioner struct{}
258 // KindForGroupVersionKinds returns false for any input.
259 func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
260 return schema.GroupVersionKind{}, false
263 // GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
264 type GroupVersioners []GroupVersioner
266 // KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occurred.
267 func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
268 for _, gv := range gvs {
269 target, ok := gv.KindForGroupVersionKinds(kinds)
275 return schema.GroupVersionKind{}, false
278 // Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
279 var _ GroupVersioner = schema.GroupVersion{}
280 var _ GroupVersioner = schema.GroupVersions{}
281 var _ GroupVersioner = multiGroupVersioner{}
283 type multiGroupVersioner struct {
284 target schema.GroupVersion
285 acceptedGroupKinds []schema.GroupKind
289 // NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
290 // Kind may be empty in the provided group kind, in which case any kind will match.
291 func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
292 if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
295 return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
298 // NewCoercingMultiGroupVersioner returns the provided group version for any incoming kind.
299 // Incoming kinds that match the provided groupKinds are preferred.
300 // Kind may be empty in the provided group kind, in which case any kind will match.
302 // gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar
303 // KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind)
305 // gv=mygroup/__internal, groupKinds=mygroup, anothergroup
306 // KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group)
308 // gv=mygroup/__internal, groupKinds=mygroup, anothergroup
309 // KindForGroupVersionKinds(yetanother/v1/Baz, yetanother/v1/Bar) -> mygroup/__internal/Baz (no preferred group/kind match, uses first kind in list)
310 func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
311 return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true}
314 // KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
315 // use the originating kind where possible.
316 func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
317 for _, src := range kinds {
318 for _, kind := range v.acceptedGroupKinds {
319 if kind.Group != src.Group {
322 if len(kind.Kind) > 0 && kind.Kind != src.Kind {
325 return v.target.WithKind(src.Kind), true
328 if v.coerce && len(kinds) > 0 {
329 return v.target.WithKind(kinds[0].Kind), true
331 return schema.GroupVersionKind{}, false