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 ¶meterCodec{ 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 = ¶meterCodec{} 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(¶meters, 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(¶meters, 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