github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 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 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 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. 15 */ 16 17 package runtime 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "net/url" 24 "reflect" 25 26 "k8s.io/kubernetes/pkg/conversion" 27 ) 28 29 // Scheme defines methods for serializing and deserializing API objects. It 30 // is an adaptation of conversion's Scheme for our API objects. 31 type Scheme struct { 32 raw *conversion.Scheme 33 // Map from version and resource to the corresponding func to convert 34 // resource field labels in that version to internal version. 35 fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc 36 } 37 38 // Function to convert a field selector to internal representation. 39 type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) 40 41 func (self *Scheme) Raw() *conversion.Scheme { 42 return self.raw 43 } 44 45 // fromScope gets the input version, desired output version, and desired Scheme 46 // from a conversion.Scope. 47 func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) { 48 scheme = self 49 inVersion = s.Meta().SrcVersion 50 outVersion = s.Meta().DestVersion 51 return inVersion, outVersion, scheme 52 } 53 54 // emptyPlugin is used to copy the Kind field to and from plugin objects. 55 type emptyPlugin struct { 56 PluginBase `json:",inline"` 57 } 58 59 // embeddedObjectToRawExtension does the conversion you would expect from the name, using the information 60 // given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins; 61 // see the comment for RawExtension. 62 func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error { 63 if in.Object == nil { 64 out.RawJSON = []byte("null") 65 return nil 66 } 67 68 // Figure out the type and kind of the output object. 69 _, outVersion, scheme := self.fromScope(s) 70 _, kind, err := scheme.raw.ObjectVersionAndKind(in.Object) 71 if err != nil { 72 return err 73 } 74 75 // Manufacture an object of this type and kind. 76 outObj, err := scheme.New(outVersion, kind) 77 if err != nil { 78 return err 79 } 80 81 // Manually do the conversion. 82 err = s.Convert(in.Object, outObj, 0) 83 if err != nil { 84 return err 85 } 86 87 // Copy the kind field into the output object. 88 err = s.Convert( 89 &emptyPlugin{PluginBase: PluginBase{Kind: kind}}, 90 outObj, 91 conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, 92 ) 93 if err != nil { 94 return err 95 } 96 // Because we provide the correct version, EncodeToVersion will not attempt a conversion. 97 raw, err := scheme.EncodeToVersion(outObj, outVersion) 98 if err != nil { 99 // TODO: if this fails, create an Unknown-- maybe some other 100 // component will understand it. 101 return err 102 } 103 out.RawJSON = raw 104 return nil 105 } 106 107 // rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information 108 // given in conversion.Scope. It's placed in all schemes as a ConversionFunc to enable plugins; 109 // see the comment for RawExtension. 110 func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error { 111 if len(in.RawJSON) == 0 || (len(in.RawJSON) == 4 && string(in.RawJSON) == "null") { 112 out.Object = nil 113 return nil 114 } 115 // Figure out the type and kind of the output object. 116 inVersion, outVersion, scheme := self.fromScope(s) 117 _, kind, err := scheme.raw.DataVersionAndKind(in.RawJSON) 118 if err != nil { 119 return err 120 } 121 122 // We have to make this object ourselves because we don't store the version field for 123 // plugin objects. 124 inObj, err := scheme.New(inVersion, kind) 125 if err != nil { 126 return err 127 } 128 129 err = scheme.DecodeInto(in.RawJSON, inObj) 130 if err != nil { 131 return err 132 } 133 134 // Make the desired internal version, and do the conversion. 135 outObj, err := scheme.New(outVersion, kind) 136 if err != nil { 137 return err 138 } 139 err = scheme.Convert(inObj, outObj) 140 if err != nil { 141 return err 142 } 143 // Last step, clear the Kind field; that should always be blank in memory. 144 err = s.Convert( 145 &emptyPlugin{PluginBase: PluginBase{Kind: ""}}, 146 outObj, 147 conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, 148 ) 149 if err != nil { 150 return err 151 } 152 out.Object = outObj 153 return nil 154 } 155 156 // runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version 157 // defined by the conversion.Scope. If objects must be encoded to different schema versions than the default, you 158 // should encode them yourself with runtime.Unknown, or convert the object prior to invoking conversion. Objects 159 // outside of the current scheme must be added as runtime.Unknown. 160 func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error { 161 src := *in 162 dest := make([]RawExtension, len(src)) 163 164 _, outVersion, scheme := self.fromScope(s) 165 166 for i := range src { 167 switch t := src[i].(type) { 168 case *Unknown: 169 // TODO: this should be decoupled from the scheme (since it is JSON specific) 170 dest[i].RawJSON = t.RawJSON 171 case *Unstructured: 172 // TODO: this should be decoupled from the scheme (since it is JSON specific) 173 data, err := json.Marshal(t.Object) 174 if err != nil { 175 return err 176 } 177 dest[i].RawJSON = data 178 default: 179 version := outVersion 180 // if the object exists 181 if inVersion, _, err := scheme.ObjectVersionAndKind(src[i]); err == nil && len(inVersion) != 0 { 182 version = inVersion 183 } 184 data, err := scheme.EncodeToVersion(src[i], version) 185 if err != nil { 186 return err 187 } 188 dest[i].RawJSON = data 189 } 190 } 191 *out = dest 192 return nil 193 } 194 195 // rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects, 196 // they are added as Unknown. 197 func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error { 198 src := *in 199 dest := make([]Object, len(src)) 200 201 _, _, scheme := self.fromScope(s) 202 203 for i := range src { 204 data := src[i].RawJSON 205 version, kind, err := scheme.raw.DataVersionAndKind(data) 206 if err != nil { 207 return err 208 } 209 dest[i] = &Unknown{ 210 TypeMeta: TypeMeta{ 211 APIVersion: version, 212 Kind: kind, 213 }, 214 RawJSON: data, 215 } 216 } 217 *out = dest 218 return nil 219 } 220 221 // NewScheme creates a new Scheme. This scheme is pluggable by default. 222 func NewScheme() *Scheme { 223 s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}} 224 s.raw.InternalVersion = "" 225 s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"} 226 if err := s.raw.AddConversionFuncs( 227 s.embeddedObjectToRawExtension, 228 s.rawExtensionToEmbeddedObject, 229 s.runtimeObjectToRawExtensionArray, 230 s.rawExtensionToRuntimeObjectArray, 231 ); err != nil { 232 panic(err) 233 } 234 // Enable map[string][]string conversions by default 235 if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil { 236 panic(err) 237 } 238 if err := s.raw.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { 239 panic(err) 240 } 241 if err := s.raw.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { 242 panic(err) 243 } 244 return s 245 } 246 247 // AddKnownTypes registers the types of the arguments to the marshaller of the package api. 248 // Encode() refuses the object unless its type is registered with AddKnownTypes. 249 func (s *Scheme) AddKnownTypes(version string, types ...Object) { 250 interfaces := make([]interface{}, len(types)) 251 for i := range types { 252 interfaces[i] = types[i] 253 } 254 s.raw.AddKnownTypes(version, interfaces...) 255 } 256 257 // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should 258 // be encoded as. Useful for testing when you don't want to make multiple packages to define 259 // your structs. 260 func (s *Scheme) AddKnownTypeWithName(version, kind string, obj Object) { 261 s.raw.AddKnownTypeWithName(version, kind, obj) 262 } 263 264 // KnownTypes returns the types known for the given version. 265 // Return value must be treated as read-only. 266 func (s *Scheme) KnownTypes(version string) map[string]reflect.Type { 267 return s.raw.KnownTypes(version) 268 } 269 270 // DataVersionAndKind will return the APIVersion and Kind of the given wire-format 271 // encoding of an API Object, or an error. 272 func (s *Scheme) DataVersionAndKind(data []byte) (version, kind string, err error) { 273 return s.raw.DataVersionAndKind(data) 274 } 275 276 // ObjectVersionAndKind returns the version and kind of the given Object. 277 func (s *Scheme) ObjectVersionAndKind(obj Object) (version, kind string, err error) { 278 return s.raw.ObjectVersionAndKind(obj) 279 } 280 281 // Recognizes returns true if the scheme is able to handle the provided version and kind 282 // of an object. 283 func (s *Scheme) Recognizes(version, kind string) bool { 284 return s.raw.Recognizes(version, kind) 285 } 286 287 // New returns a new API object of the given version ("" for internal 288 // representation) and name, or an error if it hasn't been registered. 289 func (s *Scheme) New(versionName, typeName string) (Object, error) { 290 obj, err := s.raw.NewObject(versionName, typeName) 291 if err != nil { 292 return nil, err 293 } 294 return obj.(Object), nil 295 } 296 297 // Log sets a logger on the scheme. For test purposes only 298 func (s *Scheme) Log(l conversion.DebugLogger) { 299 s.raw.Log(l) 300 } 301 302 // AddConversionFuncs adds a function to the list of conversion functions. The given 303 // function should know how to convert between two API objects. We deduce how to call 304 // it from the types of its two parameters; see the comment for 305 // Converter.RegisterConversionFunction. 306 // 307 // Note that, if you need to copy sub-objects that didn't change, it's safe to call 308 // Convert() inside your conversionFuncs, as long as you don't start a conversion 309 // chain that's infinitely recursive. 310 // 311 // Also note that the default behavior, if you don't add a conversion function, is to 312 // sanely copy fields that have the same names. It's OK if the destination type has 313 // extra fields, but it must not remove any. So you only need to add a conversion 314 // function for things with changed/removed fields. 315 func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error { 316 return s.raw.AddConversionFuncs(conversionFuncs...) 317 } 318 319 // Similar to AddConversionFuncs, but registers conversion functions that were 320 // automatically generated. 321 func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error { 322 return s.raw.AddGeneratedConversionFuncs(conversionFuncs...) 323 } 324 325 // AddDeepCopyFuncs adds a function to the list of deep-copy functions. 326 // For the expected format of deep-copy function, see the comment for 327 // Copier.RegisterDeepCopyFunction. 328 func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error { 329 return s.raw.AddDeepCopyFuncs(deepCopyFuncs...) 330 } 331 332 // Similar to AddDeepCopyFuncs, but registers deep-copy functions that were 333 // automatically generated. 334 func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error { 335 return s.raw.AddGeneratedDeepCopyFuncs(deepCopyFuncs...) 336 } 337 338 // AddFieldLabelConversionFunc adds a conversion function to convert field selectors 339 // of the given kind from the given version to internal version representation. 340 func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFunc FieldLabelConversionFunc) error { 341 if s.fieldLabelConversionFuncs[version] == nil { 342 s.fieldLabelConversionFuncs[version] = map[string]FieldLabelConversionFunc{} 343 } 344 345 s.fieldLabelConversionFuncs[version][kind] = conversionFunc 346 return nil 347 } 348 349 // AddStructFieldConversion allows you to specify a mechanical copy for a moved 350 // or renamed struct field without writing an entire conversion function. See 351 // the comment in conversion.Converter.SetStructFieldCopy for parameter details. 352 // Call as many times as needed, even on the same fields. 353 func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error { 354 return s.raw.AddStructFieldConversion(srcFieldType, srcFieldName, destFieldType, destFieldName) 355 } 356 357 // AddDefaultingFuncs adds a function to the list of value-defaulting functions. 358 // We deduce how to call it from the types of its two parameters; see the 359 // comment for Converter.RegisterDefaultingFunction. 360 func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error { 361 return s.raw.AddDefaultingFuncs(defaultingFuncs...) 362 } 363 364 // Performs a deep copy of the given object. 365 func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) { 366 return s.raw.DeepCopy(src) 367 } 368 369 // Convert will attempt to convert in into out. Both must be pointers. 370 // For easy testing of conversion functions. Returns an error if the conversion isn't 371 // possible. 372 func (s *Scheme) Convert(in, out interface{}) error { 373 return s.raw.Convert(in, out) 374 } 375 376 // Converts the given field label and value for an kind field selector from 377 // versioned representation to an unversioned one. 378 func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, string, error) { 379 if s.fieldLabelConversionFuncs[version] == nil { 380 return "", "", fmt.Errorf("No field label conversion function found for version: %s", version) 381 } 382 conversionFunc, ok := s.fieldLabelConversionFuncs[version][kind] 383 if !ok { 384 return "", "", fmt.Errorf("No field label conversion function found for version %s and kind %s", version, kind) 385 } 386 return conversionFunc(label, value) 387 } 388 389 // ConvertToVersion attempts to convert an input object to its matching Kind in another 390 // version within this scheme. Will return an error if the provided version does not 391 // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also 392 // return an error if the conversion does not result in a valid Object being 393 // returned. 394 func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) { 395 unknown, err := s.raw.ConvertToVersion(in, outVersion) 396 if err != nil { 397 return nil, err 398 } 399 obj, ok := unknown.(Object) 400 if !ok { 401 return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown) 402 } 403 return obj, nil 404 } 405 406 // EncodeToVersion turns the given api object into an appropriate JSON string. 407 // Will return an error if the object doesn't have an embedded TypeMeta. 408 // Obj may be a pointer to a struct, or a struct. If a struct, a copy 409 // must be made. If a pointer, the object may be modified before encoding, 410 // but will be put back into its original state before returning. 411 // 412 // Memory/wire format differences: 413 // * Having to keep track of the Kind and APIVersion fields makes tests 414 // very annoying, so the rule is that they are set only in wire format 415 // (json), not when in native (memory) format. This is possible because 416 // both pieces of information are implicit in the go typed object. 417 // * An exception: note that, if there are embedded API objects of known 418 // type, for example, PodList{... Items []Pod ...}, these embedded 419 // objects must be of the same version of the object they are embedded 420 // within, and their APIVersion and Kind must both be empty. 421 // * Note that the exception does not apply to the APIObject type, which 422 // recursively does Encode()/Decode(), and is capable of expressing any 423 // API object. 424 // * Only versioned objects should be encoded. This means that, if you pass 425 // a native object, Encode will convert it to a versioned object. For 426 // example, an api.Pod will get converted to a v1.Pod. However, if 427 // you pass in an object that's already versioned (v1.Pod), Encode 428 // will not modify it. 429 // 430 // The purpose of the above complex conversion behavior is to allow us to 431 // change the memory format yet not break compatibility with any stored 432 // objects, whether they be in our storage layer (e.g., etcd), or in user's 433 // config files. 434 func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) { 435 return s.raw.EncodeToVersion(obj, destVersion) 436 } 437 438 func (s *Scheme) EncodeToVersionStream(obj Object, destVersion string, stream io.Writer) error { 439 return s.raw.EncodeToVersionStream(obj, destVersion, stream) 440 } 441 442 // Decode converts a YAML or JSON string back into a pointer to an api object. 443 // Deduces the type based upon the APIVersion and Kind fields, which are set 444 // by Encode. Only versioned objects (APIVersion != "") are accepted. The object 445 // will be converted into the in-memory unversioned type before being returned. 446 func (s *Scheme) Decode(data []byte) (Object, error) { 447 obj, err := s.raw.Decode(data) 448 if err != nil { 449 return nil, err 450 } 451 return obj.(Object), nil 452 } 453 454 // DecodeToVersion converts a YAML or JSON string back into a pointer to an api 455 // object. Deduces the type based upon the APIVersion and Kind fields, which 456 // are set by Encode. Only versioned objects (APIVersion != "") are 457 // accepted. The object will be converted into the in-memory versioned type 458 // requested before being returned. 459 func (s *Scheme) DecodeToVersion(data []byte, version string) (Object, error) { 460 obj, err := s.raw.DecodeToVersion(data, version) 461 if err != nil { 462 return nil, err 463 } 464 return obj.(Object), nil 465 } 466 467 // DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error 468 // if data.Kind is set and doesn't match the type of obj. Obj should be a 469 // pointer to an api type. 470 // If obj's APIVersion doesn't match that in data, an attempt will be made to convert 471 // data into obj's version. 472 // TODO: allow Decode/DecodeInto to take a default apiVersion and a default kind, to 473 // be applied if the provided object does not have either field (integrate external 474 // apis into the decoding scheme). 475 func (s *Scheme) DecodeInto(data []byte, obj Object) error { 476 return s.raw.DecodeInto(data, obj) 477 } 478 479 func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, version, kind string) error { 480 return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, version, kind) 481 } 482 483 func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error { 484 return s.raw.DecodeParametersInto(parameters, obj) 485 } 486 487 // Copy does a deep copy of an API object. Useful mostly for tests. 488 func (s *Scheme) Copy(src Object) (Object, error) { 489 dst, err := s.raw.DeepCopy(src) 490 if err != nil { 491 return nil, err 492 } 493 return dst.(Object), nil 494 } 495 496 func (s *Scheme) CopyOrDie(obj Object) Object { 497 newObj, err := s.Copy(obj) 498 if err != nil { 499 panic(err) 500 } 501 return newObj 502 }