blob: 284e32bc3cb8eeffb38c245fc0a56e6363f5e686 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
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}