blob: 10dc12cca9143f59948b017cd6bd7a1429813cbd [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
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// DefaultingSerializer invokes defaulting after decoding.
80type DefaultingSerializer struct {
81 Defaulter ObjectDefaulter
82 Decoder Decoder
83 // Encoder is optional to allow this type to be used as both a Decoder and an Encoder
84 Encoder
85}
86
87// Decode performs a decode and then allows the defaulter to act on the provided object.
88func (d DefaultingSerializer) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
89 obj, gvk, err := d.Decoder.Decode(data, defaultGVK, into)
90 if err != nil {
91 return obj, gvk, err
92 }
93 d.Defaulter.Default(obj)
94 return obj, gvk, nil
95}
96
97// UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
98// invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
99func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk schema.GroupVersionKind, obj Object) (Object, error) {
100 if obj != nil {
101 kinds, _, err := t.ObjectKinds(obj)
102 if err != nil {
103 return nil, err
104 }
105 for _, kind := range kinds {
106 if gvk == kind {
107 return obj, nil
108 }
109 }
110 }
111 return c.New(gvk)
112}
113
114// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
115type NoopEncoder struct {
116 Decoder
117}
118
119var _ Serializer = NoopEncoder{}
120
121func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
122 return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
123}
124
125// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
126type NoopDecoder struct {
127 Encoder
128}
129
130var _ Serializer = NoopDecoder{}
131
132func (n NoopDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
133 return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder))
134}
135
136// NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
137func NewParameterCodec(scheme *Scheme) ParameterCodec {
138 return &parameterCodec{
139 typer: scheme,
140 convertor: scheme,
141 creator: scheme,
142 defaulter: scheme,
143 }
144}
145
146// parameterCodec implements conversion to and from query parameters and objects.
147type parameterCodec struct {
148 typer ObjectTyper
149 convertor ObjectConvertor
150 creator ObjectCreater
151 defaulter ObjectDefaulter
152}
153
154var _ ParameterCodec = &parameterCodec{}
155
156// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then
157// converts that object to into (if necessary). Returns an error if the operation cannot be completed.
158func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into Object) error {
159 if len(parameters) == 0 {
160 return nil
161 }
162 targetGVKs, _, err := c.typer.ObjectKinds(into)
163 if err != nil {
164 return err
165 }
166 for i := range targetGVKs {
167 if targetGVKs[i].GroupVersion() == from {
168 if err := c.convertor.Convert(&parameters, into, nil); err != nil {
169 return err
170 }
171 // in the case where we going into the same object we're receiving, default on the outbound object
172 if c.defaulter != nil {
173 c.defaulter.Default(into)
174 }
175 return nil
176 }
177 }
178
179 input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
180 if err != nil {
181 return err
182 }
183 if err := c.convertor.Convert(&parameters, input, nil); err != nil {
184 return err
185 }
186 // if we have defaulter, default the input before converting to output
187 if c.defaulter != nil {
188 c.defaulter.Default(input)
189 }
190 return c.convertor.Convert(input, into, nil)
191}
192
193// EncodeParameters converts the provided object into the to version, then converts that object to url.Values.
194// Returns an error if conversion is not possible.
195func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (url.Values, error) {
196 gvks, _, err := c.typer.ObjectKinds(obj)
197 if err != nil {
198 return nil, err
199 }
200 gvk := gvks[0]
201 if to != gvk.GroupVersion() {
202 out, err := c.convertor.ConvertToVersion(obj, to)
203 if err != nil {
204 return nil, err
205 }
206 obj = out
207 }
208 return queryparams.Convert(obj)
209}
210
211type base64Serializer struct {
212 Encoder
213 Decoder
214}
215
216func NewBase64Serializer(e Encoder, d Decoder) Serializer {
217 return &base64Serializer{e, d}
218}
219
220func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
221 e := base64.NewEncoder(base64.StdEncoding, stream)
222 err := s.Encoder.Encode(obj, e)
223 e.Close()
224 return err
225}
226
227func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
228 out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
229 n, err := base64.StdEncoding.Decode(out, data)
230 if err != nil {
231 return nil, nil, err
232 }
233 return s.Decoder.Decode(out[:n], defaults, into)
234}
235
236// SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot
237// include media-type parameters), or the first info with an empty media type, or false if no type matches.
238func SerializerInfoForMediaType(types []SerializerInfo, mediaType string) (SerializerInfo, bool) {
239 for _, info := range types {
240 if info.MediaType == mediaType {
241 return info, true
242 }
243 }
244 for _, info := range types {
245 if len(info.MediaType) == 0 {
246 return info, true
247 }
248 }
249 return SerializerInfo{}, false
250}
251
252var (
253 // InternalGroupVersioner will always prefer the internal version for a given group version kind.
254 InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
255 // DisabledGroupVersioner will reject all kinds passed to it.
256 DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
257)
258
259type internalGroupVersioner struct{}
260
261// KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
262func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
263 for _, kind := range kinds {
264 if kind.Version == APIVersionInternal {
265 return kind, true
266 }
267 }
268 for _, kind := range kinds {
269 return schema.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
270 }
271 return schema.GroupVersionKind{}, false
272}
273
274type disabledGroupVersioner struct{}
275
276// KindForGroupVersionKinds returns false for any input.
277func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
278 return schema.GroupVersionKind{}, false
279}
280
281// GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
282type GroupVersioners []GroupVersioner
283
284// KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occurred.
285func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
286 for _, gv := range gvs {
287 target, ok := gv.KindForGroupVersionKinds(kinds)
288 if !ok {
289 continue
290 }
291 return target, true
292 }
293 return schema.GroupVersionKind{}, false
294}
295
296// Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
297var _ GroupVersioner = schema.GroupVersion{}
298var _ GroupVersioner = schema.GroupVersions{}
299var _ GroupVersioner = multiGroupVersioner{}
300
301type multiGroupVersioner struct {
302 target schema.GroupVersion
303 acceptedGroupKinds []schema.GroupKind
304}
305
306// NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
307// Kind may be empty in the provided group kind, in which case any kind will match.
308func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
309 if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
310 return gv
311 }
312 return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
313}
314
315// KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
316// use the originating kind where possible.
317func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
318 for _, src := range kinds {
319 for _, kind := range v.acceptedGroupKinds {
320 if kind.Group != src.Group {
321 continue
322 }
323 if len(kind.Kind) > 0 && kind.Kind != src.Kind {
324 continue
325 }
326 return v.target.WithKind(src.Kind), true
327 }
328 }
329 return schema.GroupVersionKind{}, false
330}