1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package runtime
18
19import (
20	"bytes"
21	"encoding/base64"
22	"fmt"
23	"io"
24	"net/url"
25	"reflect"
26
27	"k8s.io/apimachinery/pkg/conversion/queryparams"
28	"k8s.io/apimachinery/pkg/runtime/schema"
29)
30
31// codec binds an encoder and decoder.
32type codec struct {
33	Encoder
34	Decoder
35}
36
37// NewCodec creates a Codec from an Encoder and Decoder.
38func NewCodec(e Encoder, d Decoder) Codec {
39	return codec{e, d}
40}
41
42// Encode is a convenience wrapper for encoding to a []byte from an Encoder
43func Encode(e Encoder, obj Object) ([]byte, error) {
44	// TODO: reuse buffer
45	buf := &bytes.Buffer{}
46	if err := e.Encode(obj, buf); err != nil {
47		return nil, err
48	}
49	return buf.Bytes(), nil
50}
51
52// Decode is a convenience wrapper for decoding data into an Object.
53func Decode(d Decoder, data []byte) (Object, error) {
54	obj, _, err := d.Decode(data, nil, nil)
55	return obj, err
56}
57
58// DecodeInto performs a Decode into the provided object.
59func DecodeInto(d Decoder, data []byte, into Object) error {
60	out, gvk, err := d.Decode(data, nil, into)
61	if err != nil {
62		return err
63	}
64	if out != into {
65		return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into))
66	}
67	return nil
68}
69
70// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
71func EncodeOrDie(e Encoder, obj Object) string {
72	bytes, err := Encode(e, obj)
73	if err != nil {
74		panic(err)
75	}
76	return string(bytes)
77}
78
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.
81func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk schema.GroupVersionKind, obj Object) (Object, error) {
82	if obj != nil {
83		kinds, _, err := t.ObjectKinds(obj)
84		if err != nil {
85			return nil, err
86		}
87		for _, kind := range kinds {
88			if gvk == kind {
89				return obj, nil
90			}
91		}
92	}
93	return c.New(gvk)
94}
95
96// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
97type NoopEncoder struct {
98	Decoder
99}
100
101var _ Serializer = NoopEncoder{}
102
103func (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))
105}
106
107// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
108type NoopDecoder struct {
109	Encoder
110}
111
112var _ Serializer = NoopDecoder{}
113
114func (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))
116}
117
118// NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
119func NewParameterCodec(scheme *Scheme) ParameterCodec {
120	return &parameterCodec{
121		typer:     scheme,
122		convertor: scheme,
123		creator:   scheme,
124		defaulter: scheme,
125	}
126}
127
128// parameterCodec implements conversion to and from query parameters and objects.
129type parameterCodec struct {
130	typer     ObjectTyper
131	convertor ObjectConvertor
132	creator   ObjectCreater
133	defaulter ObjectDefaulter
134}
135
136var _ ParameterCodec = &parameterCodec{}
137
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.
140func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into Object) error {
141	if len(parameters) == 0 {
142		return nil
143	}
144	targetGVKs, _, err := c.typer.ObjectKinds(into)
145	if err != nil {
146		return err
147	}
148	for i := range targetGVKs {
149		if targetGVKs[i].GroupVersion() == from {
150			if err := c.convertor.Convert(&parameters, into, nil); err != nil {
151				return err
152			}
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)
156			}
157			return nil
158		}
159	}
160
161	input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
162	if err != nil {
163		return err
164	}
165	if err := c.convertor.Convert(&parameters, input, nil); err != nil {
166		return err
167	}
168	// if we have defaulter, default the input before converting to output
169	if c.defaulter != nil {
170		c.defaulter.Default(input)
171	}
172	return c.convertor.Convert(input, into, nil)
173}
174
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.
177func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (url.Values, error) {
178	gvks, _, err := c.typer.ObjectKinds(obj)
179	if err != nil {
180		return nil, err
181	}
182	gvk := gvks[0]
183	if to != gvk.GroupVersion() {
184		out, err := c.convertor.ConvertToVersion(obj, to)
185		if err != nil {
186			return nil, err
187		}
188		obj = out
189	}
190	return queryparams.Convert(obj)
191}
192
193type base64Serializer struct {
194	Encoder
195	Decoder
196}
197
198func NewBase64Serializer(e Encoder, d Decoder) Serializer {
199	return &base64Serializer{e, d}
200}
201
202func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
203	e := base64.NewEncoder(base64.StdEncoding, stream)
204	err := s.Encoder.Encode(obj, e)
205	e.Close()
206	return err
207}
208
209func (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)
212	if err != nil {
213		return nil, nil, err
214	}
215	return s.Decoder.Decode(out[:n], defaults, into)
216}
217
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.
220func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
221	for _, info := range types {
222		if info.MediaType == mediaType {
223			return info, true
224		}
225	}
226	for _, info := range types {
227		if len(info.MediaType) == 0 {
228			return info, true
229		}
230	}
231	return SerializerInfo{}, false
232}
233
234var (
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{}
239)
240
241type internalGroupVersioner struct{}
242
243// KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
244func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
245	for _, kind := range kinds {
246		if kind.Version == APIVersionInternal {
247			return kind, true
248		}
249	}
250	for _, kind := range kinds {
251		return schema.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
252	}
253	return schema.GroupVersionKind{}, false
254}
255
256type disabledGroupVersioner struct{}
257
258// KindForGroupVersionKinds returns false for any input.
259func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
260	return schema.GroupVersionKind{}, false
261}
262
263// GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
264type GroupVersioners []GroupVersioner
265
266// KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occurred.
267func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
268	for _, gv := range gvs {
269		target, ok := gv.KindForGroupVersionKinds(kinds)
270		if !ok {
271			continue
272		}
273		return target, true
274	}
275	return schema.GroupVersionKind{}, false
276}
277
278// Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
279var _ GroupVersioner = schema.GroupVersion{}
280var _ GroupVersioner = schema.GroupVersions{}
281var _ GroupVersioner = multiGroupVersioner{}
282
283type multiGroupVersioner struct {
284	target             schema.GroupVersion
285	acceptedGroupKinds []schema.GroupKind
286	coerce             bool
287}
288
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.
291func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
292	if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
293		return gv
294	}
295	return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
296}
297
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.
301// Examples:
302//   gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar
303//   KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind)
304//
305//   gv=mygroup/__internal, groupKinds=mygroup, anothergroup
306//   KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group)
307//
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)
310func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
311	return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true}
312}
313
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.
316func (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 {
320				continue
321			}
322			if len(kind.Kind) > 0 && kind.Kind != src.Kind {
323				continue
324			}
325			return v.target.WithKind(src.Kind), true
326		}
327	}
328	if v.coerce && len(kinds) > 0 {
329		return v.target.WithKind(kinds[0].Kind), true
330	}
331	return schema.GroupVersionKind{}, false
332}
333