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  }