...

Source file src/k8s.io/apimachinery/pkg/runtime/scheme.go

Documentation: k8s.io/apimachinery/pkg/runtime

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     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  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  
    24  	"k8s.io/apimachinery/pkg/conversion"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	"k8s.io/apimachinery/pkg/util/naming"
    27  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  )
    30  
    31  // Scheme defines methods for serializing and deserializing API objects, a type
    32  // registry for converting group, version, and kind information to and from Go
    33  // schemas, and mappings between Go schemas of different versions. A scheme is the
    34  // foundation for a versioned API and versioned configuration over time.
    35  //
    36  // In a Scheme, a Type is a particular Go struct, a Version is a point-in-time
    37  // identifier for a particular representation of that Type (typically backwards
    38  // compatible), a Kind is the unique name for that Type within the Version, and a
    39  // Group identifies a set of Versions, Kinds, and Types that evolve over time. An
    40  // Unversioned Type is one that is not yet formally bound to a type and is promised
    41  // to be backwards compatible (effectively a "v1" of a Type that does not expect
    42  // to break in the future).
    43  //
    44  // Schemes are not expected to change at runtime and are only threadsafe after
    45  // registration is complete.
    46  type Scheme struct {
    47  	// gvkToType allows one to figure out the go type of an object with
    48  	// the given version and name.
    49  	gvkToType map[schema.GroupVersionKind]reflect.Type
    50  
    51  	// typeToGVK allows one to find metadata for a given go object.
    52  	// The reflect.Type we index by should *not* be a pointer.
    53  	typeToGVK map[reflect.Type][]schema.GroupVersionKind
    54  
    55  	// unversionedTypes are transformed without conversion in ConvertToVersion.
    56  	unversionedTypes map[reflect.Type]schema.GroupVersionKind
    57  
    58  	// unversionedKinds are the names of kinds that can be created in the context of any group
    59  	// or version
    60  	// TODO: resolve the status of unversioned types.
    61  	unversionedKinds map[string]reflect.Type
    62  
    63  	// Map from version and resource to the corresponding func to convert
    64  	// resource field labels in that version to internal version.
    65  	fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
    66  
    67  	// defaulterFuncs is a map to funcs to be called with an object to provide defaulting
    68  	// the provided object must be a pointer.
    69  	defaulterFuncs map[reflect.Type]func(interface{})
    70  
    71  	// converter stores all registered conversion functions. It also has
    72  	// default converting behavior.
    73  	converter *conversion.Converter
    74  
    75  	// versionPriority is a map of groups to ordered lists of versions for those groups indicating the
    76  	// default priorities of these versions as registered in the scheme
    77  	versionPriority map[string][]string
    78  
    79  	// observedVersions keeps track of the order we've seen versions during type registration
    80  	observedVersions []schema.GroupVersion
    81  
    82  	// schemeName is the name of this scheme.  If you don't specify a name, the stack of the NewScheme caller will be used.
    83  	// This is useful for error reporting to indicate the origin of the scheme.
    84  	schemeName string
    85  }
    86  
    87  // FieldLabelConversionFunc converts a field selector to internal representation.
    88  type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
    89  
    90  // NewScheme creates a new Scheme. This scheme is pluggable by default.
    91  func NewScheme() *Scheme {
    92  	s := &Scheme{
    93  		gvkToType:                 map[schema.GroupVersionKind]reflect.Type{},
    94  		typeToGVK:                 map[reflect.Type][]schema.GroupVersionKind{},
    95  		unversionedTypes:          map[reflect.Type]schema.GroupVersionKind{},
    96  		unversionedKinds:          map[string]reflect.Type{},
    97  		fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},
    98  		defaulterFuncs:            map[reflect.Type]func(interface{}){},
    99  		versionPriority:           map[string][]string{},
   100  		schemeName:                naming.GetNameFromCallsite(internalPackages...),
   101  	}
   102  	s.converter = conversion.NewConverter(nil)
   103  
   104  	// Enable couple default conversions by default.
   105  	utilruntime.Must(RegisterEmbeddedConversions(s))
   106  	utilruntime.Must(RegisterStringConversions(s))
   107  	return s
   108  }
   109  
   110  // Converter allows access to the converter for the scheme
   111  func (s *Scheme) Converter() *conversion.Converter {
   112  	return s.converter
   113  }
   114  
   115  // AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
   116  // Whenever an object of this type is serialized, it is serialized with the provided group version and is not
   117  // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an
   118  // API group and version that would never be updated.
   119  //
   120  // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into
   121  // every version with particular schemas. Resolve this method at that point.
   122  func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
   123  	s.addObservedVersion(version)
   124  	s.AddKnownTypes(version, types...)
   125  	for _, obj := range types {
   126  		t := reflect.TypeOf(obj).Elem()
   127  		gvk := version.WithKind(t.Name())
   128  		s.unversionedTypes[t] = gvk
   129  		if old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {
   130  			panic(fmt.Sprintf("%v.%v has already been registered as unversioned kind %q - kind name must be unique in scheme %q", old.PkgPath(), old.Name(), gvk, s.schemeName))
   131  		}
   132  		s.unversionedKinds[gvk.Kind] = t
   133  	}
   134  }
   135  
   136  // AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
   137  // All objects passed to types should be pointers to structs. The name that go reports for
   138  // the struct becomes the "kind" field when encoding. Version may not be empty - use the
   139  // APIVersionInternal constant if you have a type that does not have a formal version.
   140  func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
   141  	s.addObservedVersion(gv)
   142  	for _, obj := range types {
   143  		t := reflect.TypeOf(obj)
   144  		if t.Kind() != reflect.Pointer {
   145  			panic("All types must be pointers to structs.")
   146  		}
   147  		t = t.Elem()
   148  		s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
   149  	}
   150  }
   151  
   152  // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
   153  // be encoded as. Useful for testing when you don't want to make multiple packages to define
   154  // your structs. Version may not be empty - use the APIVersionInternal constant if you have a
   155  // type that does not have a formal version.
   156  func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
   157  	s.addObservedVersion(gvk.GroupVersion())
   158  	t := reflect.TypeOf(obj)
   159  	if len(gvk.Version) == 0 {
   160  		panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
   161  	}
   162  	if t.Kind() != reflect.Pointer {
   163  		panic("All types must be pointers to structs.")
   164  	}
   165  	t = t.Elem()
   166  	if t.Kind() != reflect.Struct {
   167  		panic("All types must be pointers to structs.")
   168  	}
   169  
   170  	if oldT, found := s.gvkToType[gvk]; found && oldT != t {
   171  		panic(fmt.Sprintf("Double registration of different types for %v: old=%v.%v, new=%v.%v in scheme %q", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name(), s.schemeName))
   172  	}
   173  
   174  	s.gvkToType[gvk] = t
   175  
   176  	for _, existingGvk := range s.typeToGVK[t] {
   177  		if existingGvk == gvk {
   178  			return
   179  		}
   180  	}
   181  	s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
   182  
   183  	// if the type implements DeepCopyInto(<obj>), register a self-conversion
   184  	if m := reflect.ValueOf(obj).MethodByName("DeepCopyInto"); m.IsValid() && m.Type().NumIn() == 1 && m.Type().NumOut() == 0 && m.Type().In(0) == reflect.TypeOf(obj) {
   185  		if err := s.AddGeneratedConversionFunc(obj, obj, func(a, b interface{}, scope conversion.Scope) error {
   186  			// copy a to b
   187  			reflect.ValueOf(a).MethodByName("DeepCopyInto").Call([]reflect.Value{reflect.ValueOf(b)})
   188  			// clear TypeMeta to match legacy reflective conversion
   189  			b.(Object).GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
   190  			return nil
   191  		}); err != nil {
   192  			panic(err)
   193  		}
   194  	}
   195  }
   196  
   197  // KnownTypes returns the types known for the given version.
   198  func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {
   199  	types := make(map[string]reflect.Type)
   200  	for gvk, t := range s.gvkToType {
   201  		if gv != gvk.GroupVersion() {
   202  			continue
   203  		}
   204  
   205  		types[gvk.Kind] = t
   206  	}
   207  	return types
   208  }
   209  
   210  // VersionsForGroupKind returns the versions that a particular GroupKind can be converted to within the given group.
   211  // A GroupKind might be converted to a different group. That information is available in EquivalentResourceMapper.
   212  func (s *Scheme) VersionsForGroupKind(gk schema.GroupKind) []schema.GroupVersion {
   213  	availableVersions := []schema.GroupVersion{}
   214  	for gvk := range s.gvkToType {
   215  		if gk != gvk.GroupKind() {
   216  			continue
   217  		}
   218  
   219  		availableVersions = append(availableVersions, gvk.GroupVersion())
   220  	}
   221  
   222  	// order the return for stability
   223  	ret := []schema.GroupVersion{}
   224  	for _, version := range s.PrioritizedVersionsForGroup(gk.Group) {
   225  		for _, availableVersion := range availableVersions {
   226  			if version != availableVersion {
   227  				continue
   228  			}
   229  			ret = append(ret, availableVersion)
   230  		}
   231  	}
   232  
   233  	return ret
   234  }
   235  
   236  // AllKnownTypes returns the all known types.
   237  func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
   238  	return s.gvkToType
   239  }
   240  
   241  // ObjectKinds returns all possible group,version,kind of the go object, true if the
   242  // object is considered unversioned, or an error if it's not a pointer or is unregistered.
   243  func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
   244  	// Unstructured objects are always considered to have their declared GVK
   245  	if _, ok := obj.(Unstructured); ok {
   246  		// we require that the GVK be populated in order to recognize the object
   247  		gvk := obj.GetObjectKind().GroupVersionKind()
   248  		if len(gvk.Kind) == 0 {
   249  			return nil, false, NewMissingKindErr("unstructured object has no kind")
   250  		}
   251  		if len(gvk.Version) == 0 {
   252  			return nil, false, NewMissingVersionErr("unstructured object has no version")
   253  		}
   254  		return []schema.GroupVersionKind{gvk}, false, nil
   255  	}
   256  
   257  	v, err := conversion.EnforcePtr(obj)
   258  	if err != nil {
   259  		return nil, false, err
   260  	}
   261  	t := v.Type()
   262  
   263  	gvks, ok := s.typeToGVK[t]
   264  	if !ok {
   265  		return nil, false, NewNotRegisteredErrForType(s.schemeName, t)
   266  	}
   267  	_, unversionedType := s.unversionedTypes[t]
   268  
   269  	return gvks, unversionedType, nil
   270  }
   271  
   272  // Recognizes returns true if the scheme is able to handle the provided group,version,kind
   273  // of an object.
   274  func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {
   275  	_, exists := s.gvkToType[gvk]
   276  	return exists
   277  }
   278  
   279  func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
   280  	v, err := conversion.EnforcePtr(obj)
   281  	if err != nil {
   282  		return false, false
   283  	}
   284  	t := v.Type()
   285  
   286  	if _, ok := s.typeToGVK[t]; !ok {
   287  		return false, false
   288  	}
   289  	_, ok := s.unversionedTypes[t]
   290  	return ok, true
   291  }
   292  
   293  // New returns a new API object of the given version and name, or an error if it hasn't
   294  // been registered. The version and kind fields must be specified.
   295  func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
   296  	if t, exists := s.gvkToType[kind]; exists {
   297  		return reflect.New(t).Interface().(Object), nil
   298  	}
   299  
   300  	if t, exists := s.unversionedKinds[kind.Kind]; exists {
   301  		return reflect.New(t).Interface().(Object), nil
   302  	}
   303  	return nil, NewNotRegisteredErrForKind(s.schemeName, kind)
   304  }
   305  
   306  // AddIgnoredConversionType identifies a pair of types that should be skipped by
   307  // conversion (because the data inside them is explicitly dropped during
   308  // conversion).
   309  func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
   310  	return s.converter.RegisterIgnoredConversion(from, to)
   311  }
   312  
   313  // AddConversionFunc registers a function that converts between a and b by passing objects of those
   314  // types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
   315  // any other guarantee.
   316  func (s *Scheme) AddConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
   317  	return s.converter.RegisterUntypedConversionFunc(a, b, fn)
   318  }
   319  
   320  // AddGeneratedConversionFunc registers a function that converts between a and b by passing objects of those
   321  // types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
   322  // any other guarantee.
   323  func (s *Scheme) AddGeneratedConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
   324  	return s.converter.RegisterGeneratedUntypedConversionFunc(a, b, fn)
   325  }
   326  
   327  // AddFieldLabelConversionFunc adds a conversion function to convert field selectors
   328  // of the given kind from the given version to internal version representation.
   329  func (s *Scheme) AddFieldLabelConversionFunc(gvk schema.GroupVersionKind, conversionFunc FieldLabelConversionFunc) error {
   330  	s.fieldLabelConversionFuncs[gvk] = conversionFunc
   331  	return nil
   332  }
   333  
   334  // AddTypeDefaultingFunc registers a function that is passed a pointer to an
   335  // object and can default fields on the object. These functions will be invoked
   336  // when Default() is called. The function will never be called unless the
   337  // defaulted object matches srcType. If this function is invoked twice with the
   338  // same srcType, the fn passed to the later call will be used instead.
   339  func (s *Scheme) AddTypeDefaultingFunc(srcType Object, fn func(interface{})) {
   340  	s.defaulterFuncs[reflect.TypeOf(srcType)] = fn
   341  }
   342  
   343  // Default sets defaults on the provided Object.
   344  func (s *Scheme) Default(src Object) {
   345  	if fn, ok := s.defaulterFuncs[reflect.TypeOf(src)]; ok {
   346  		fn(src)
   347  	}
   348  }
   349  
   350  // Convert will attempt to convert in into out. Both must be pointers. For easy
   351  // testing of conversion functions. Returns an error if the conversion isn't
   352  // possible. You can call this with types that haven't been registered (for example,
   353  // a to test conversion of types that are nested within registered types). The
   354  // context interface is passed to the convertor. Convert also supports Unstructured
   355  // types and will convert them intelligently.
   356  func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
   357  	unstructuredIn, okIn := in.(Unstructured)
   358  	unstructuredOut, okOut := out.(Unstructured)
   359  	switch {
   360  	case okIn && okOut:
   361  		// converting unstructured input to an unstructured output is a straight copy - unstructured
   362  		// is a "smart holder" and the contents are passed by reference between the two objects
   363  		unstructuredOut.SetUnstructuredContent(unstructuredIn.UnstructuredContent())
   364  		return nil
   365  
   366  	case okOut:
   367  		// if the output is an unstructured object, use the standard Go type to unstructured
   368  		// conversion. The object must not be internal.
   369  		obj, ok := in.(Object)
   370  		if !ok {
   371  			return fmt.Errorf("unable to convert object type %T to Unstructured, must be a runtime.Object", in)
   372  		}
   373  		gvks, unversioned, err := s.ObjectKinds(obj)
   374  		if err != nil {
   375  			return err
   376  		}
   377  		gvk := gvks[0]
   378  
   379  		// if no conversion is necessary, convert immediately
   380  		if unversioned || gvk.Version != APIVersionInternal {
   381  			content, err := DefaultUnstructuredConverter.ToUnstructured(in)
   382  			if err != nil {
   383  				return err
   384  			}
   385  			unstructuredOut.SetUnstructuredContent(content)
   386  			unstructuredOut.GetObjectKind().SetGroupVersionKind(gvk)
   387  			return nil
   388  		}
   389  
   390  		// attempt to convert the object to an external version first.
   391  		target, ok := context.(GroupVersioner)
   392  		if !ok {
   393  			return fmt.Errorf("unable to convert the internal object type %T to Unstructured without providing a preferred version to convert to", in)
   394  		}
   395  		// Convert is implicitly unsafe, so we don't need to perform a safe conversion
   396  		versioned, err := s.UnsafeConvertToVersion(obj, target)
   397  		if err != nil {
   398  			return err
   399  		}
   400  		content, err := DefaultUnstructuredConverter.ToUnstructured(versioned)
   401  		if err != nil {
   402  			return err
   403  		}
   404  		unstructuredOut.SetUnstructuredContent(content)
   405  		return nil
   406  
   407  	case okIn:
   408  		// converting an unstructured object to any type is modeled by first converting
   409  		// the input to a versioned type, then running standard conversions
   410  		typed, err := s.unstructuredToTyped(unstructuredIn)
   411  		if err != nil {
   412  			return err
   413  		}
   414  		in = typed
   415  	}
   416  
   417  	meta := s.generateConvertMeta(in)
   418  	meta.Context = context
   419  	return s.converter.Convert(in, out, meta)
   420  }
   421  
   422  // ConvertFieldLabel alters the given field label and value for an kind field selector from
   423  // versioned representation to an unversioned one or returns an error.
   424  func (s *Scheme) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
   425  	conversionFunc, ok := s.fieldLabelConversionFuncs[gvk]
   426  	if !ok {
   427  		return DefaultMetaV1FieldSelectorConversion(label, value)
   428  	}
   429  	return conversionFunc(label, value)
   430  }
   431  
   432  // ConvertToVersion attempts to convert an input object to its matching Kind in another
   433  // version within this scheme. Will return an error if the provided version does not
   434  // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
   435  // return an error if the conversion does not result in a valid Object being
   436  // returned. Passes target down to the conversion methods as the Context on the scope.
   437  func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
   438  	return s.convertToVersion(true, in, target)
   439  }
   440  
   441  // UnsafeConvertToVersion will convert in to the provided target if such a conversion is possible,
   442  // but does not guarantee the output object does not share fields with the input object. It attempts to be as
   443  // efficient as possible when doing conversion.
   444  func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
   445  	return s.convertToVersion(false, in, target)
   446  }
   447  
   448  // convertToVersion handles conversion with an optional copy.
   449  func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error) {
   450  	var t reflect.Type
   451  
   452  	if u, ok := in.(Unstructured); ok {
   453  		typed, err := s.unstructuredToTyped(u)
   454  		if err != nil {
   455  			return nil, err
   456  		}
   457  
   458  		in = typed
   459  		// unstructuredToTyped returns an Object, which must be a pointer to a struct.
   460  		t = reflect.TypeOf(in).Elem()
   461  
   462  	} else {
   463  		// determine the incoming kinds with as few allocations as possible.
   464  		t = reflect.TypeOf(in)
   465  		if t.Kind() != reflect.Pointer {
   466  			return nil, fmt.Errorf("only pointer types may be converted: %v", t)
   467  		}
   468  		t = t.Elem()
   469  		if t.Kind() != reflect.Struct {
   470  			return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
   471  		}
   472  	}
   473  
   474  	kinds, ok := s.typeToGVK[t]
   475  	if !ok || len(kinds) == 0 {
   476  		return nil, NewNotRegisteredErrForType(s.schemeName, t)
   477  	}
   478  
   479  	gvk, ok := target.KindForGroupVersionKinds(kinds)
   480  	if !ok {
   481  		// try to see if this type is listed as unversioned (for legacy support)
   482  		// TODO: when we move to server API versions, we should completely remove the unversioned concept
   483  		if unversionedKind, ok := s.unversionedTypes[t]; ok {
   484  			if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
   485  				return copyAndSetTargetKind(copy, in, gvk)
   486  			}
   487  			return copyAndSetTargetKind(copy, in, unversionedKind)
   488  		}
   489  		return nil, NewNotRegisteredErrForTarget(s.schemeName, t, target)
   490  	}
   491  
   492  	// target wants to use the existing type, set kind and return (no conversion necessary)
   493  	for _, kind := range kinds {
   494  		if gvk == kind {
   495  			return copyAndSetTargetKind(copy, in, gvk)
   496  		}
   497  	}
   498  
   499  	// type is unversioned, no conversion necessary
   500  	if unversionedKind, ok := s.unversionedTypes[t]; ok {
   501  		if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
   502  			return copyAndSetTargetKind(copy, in, gvk)
   503  		}
   504  		return copyAndSetTargetKind(copy, in, unversionedKind)
   505  	}
   506  
   507  	out, err := s.New(gvk)
   508  	if err != nil {
   509  		return nil, err
   510  	}
   511  
   512  	if copy {
   513  		in = in.DeepCopyObject()
   514  	}
   515  
   516  	meta := s.generateConvertMeta(in)
   517  	meta.Context = target
   518  	if err := s.converter.Convert(in, out, meta); err != nil {
   519  		return nil, err
   520  	}
   521  
   522  	setTargetKind(out, gvk)
   523  	return out, nil
   524  }
   525  
   526  // unstructuredToTyped attempts to transform an unstructured object to a typed
   527  // object if possible. It will return an error if conversion is not possible, or the versioned
   528  // Go form of the object. Note that this conversion will lose fields.
   529  func (s *Scheme) unstructuredToTyped(in Unstructured) (Object, error) {
   530  	// the type must be something we recognize
   531  	gvks, _, err := s.ObjectKinds(in)
   532  	if err != nil {
   533  		return nil, err
   534  	}
   535  	typed, err := s.New(gvks[0])
   536  	if err != nil {
   537  		return nil, err
   538  	}
   539  	if err := DefaultUnstructuredConverter.FromUnstructured(in.UnstructuredContent(), typed); err != nil {
   540  		return nil, fmt.Errorf("unable to convert unstructured object to %v: %v", gvks[0], err)
   541  	}
   542  	return typed, nil
   543  }
   544  
   545  // generateConvertMeta constructs the meta value we pass to Convert.
   546  func (s *Scheme) generateConvertMeta(in interface{}) *conversion.Meta {
   547  	return s.converter.DefaultMeta(reflect.TypeOf(in))
   548  }
   549  
   550  // copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
   551  func copyAndSetTargetKind(copy bool, obj Object, kind schema.GroupVersionKind) (Object, error) {
   552  	if copy {
   553  		obj = obj.DeepCopyObject()
   554  	}
   555  	setTargetKind(obj, kind)
   556  	return obj, nil
   557  }
   558  
   559  // setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
   560  func setTargetKind(obj Object, kind schema.GroupVersionKind) {
   561  	if kind.Version == APIVersionInternal {
   562  		// internal is a special case
   563  		// TODO: look at removing the need to special case this
   564  		obj.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
   565  		return
   566  	}
   567  	obj.GetObjectKind().SetGroupVersionKind(kind)
   568  }
   569  
   570  // SetVersionPriority allows specifying a precise order of priority. All specified versions must be in the same group,
   571  // and the specified order overwrites any previously specified order for this group
   572  func (s *Scheme) SetVersionPriority(versions ...schema.GroupVersion) error {
   573  	groups := sets.String{}
   574  	order := []string{}
   575  	for _, version := range versions {
   576  		if len(version.Version) == 0 || version.Version == APIVersionInternal {
   577  			return fmt.Errorf("internal versions cannot be prioritized: %v", version)
   578  		}
   579  
   580  		groups.Insert(version.Group)
   581  		order = append(order, version.Version)
   582  	}
   583  	if len(groups) != 1 {
   584  		return fmt.Errorf("must register versions for exactly one group: %v", strings.Join(groups.List(), ", "))
   585  	}
   586  
   587  	s.versionPriority[groups.List()[0]] = order
   588  	return nil
   589  }
   590  
   591  // PrioritizedVersionsForGroup returns versions for a single group in priority order
   592  func (s *Scheme) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {
   593  	ret := []schema.GroupVersion{}
   594  	for _, version := range s.versionPriority[group] {
   595  		ret = append(ret, schema.GroupVersion{Group: group, Version: version})
   596  	}
   597  	for _, observedVersion := range s.observedVersions {
   598  		if observedVersion.Group != group {
   599  			continue
   600  		}
   601  		found := false
   602  		for _, existing := range ret {
   603  			if existing == observedVersion {
   604  				found = true
   605  				break
   606  			}
   607  		}
   608  		if !found {
   609  			ret = append(ret, observedVersion)
   610  		}
   611  	}
   612  
   613  	return ret
   614  }
   615  
   616  // PrioritizedVersionsAllGroups returns all known versions in their priority order.  Groups are random, but
   617  // versions for a single group are prioritized
   618  func (s *Scheme) PrioritizedVersionsAllGroups() []schema.GroupVersion {
   619  	ret := []schema.GroupVersion{}
   620  	for group, versions := range s.versionPriority {
   621  		for _, version := range versions {
   622  			ret = append(ret, schema.GroupVersion{Group: group, Version: version})
   623  		}
   624  	}
   625  	for _, observedVersion := range s.observedVersions {
   626  		found := false
   627  		for _, existing := range ret {
   628  			if existing == observedVersion {
   629  				found = true
   630  				break
   631  			}
   632  		}
   633  		if !found {
   634  			ret = append(ret, observedVersion)
   635  		}
   636  	}
   637  	return ret
   638  }
   639  
   640  // PreferredVersionAllGroups returns the most preferred version for every group.
   641  // group ordering is random.
   642  func (s *Scheme) PreferredVersionAllGroups() []schema.GroupVersion {
   643  	ret := []schema.GroupVersion{}
   644  	for group, versions := range s.versionPriority {
   645  		for _, version := range versions {
   646  			ret = append(ret, schema.GroupVersion{Group: group, Version: version})
   647  			break
   648  		}
   649  	}
   650  	for _, observedVersion := range s.observedVersions {
   651  		found := false
   652  		for _, existing := range ret {
   653  			if existing.Group == observedVersion.Group {
   654  				found = true
   655  				break
   656  			}
   657  		}
   658  		if !found {
   659  			ret = append(ret, observedVersion)
   660  		}
   661  	}
   662  
   663  	return ret
   664  }
   665  
   666  // IsGroupRegistered returns true if types for the group have been registered with the scheme
   667  func (s *Scheme) IsGroupRegistered(group string) bool {
   668  	for _, observedVersion := range s.observedVersions {
   669  		if observedVersion.Group == group {
   670  			return true
   671  		}
   672  	}
   673  	return false
   674  }
   675  
   676  // IsVersionRegistered returns true if types for the version have been registered with the scheme
   677  func (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {
   678  	for _, observedVersion := range s.observedVersions {
   679  		if observedVersion == version {
   680  			return true
   681  		}
   682  	}
   683  
   684  	return false
   685  }
   686  
   687  func (s *Scheme) addObservedVersion(version schema.GroupVersion) {
   688  	if len(version.Version) == 0 || version.Version == APIVersionInternal {
   689  		return
   690  	}
   691  	for _, observedVersion := range s.observedVersions {
   692  		if observedVersion == version {
   693  			return
   694  		}
   695  	}
   696  
   697  	s.observedVersions = append(s.observedVersions, version)
   698  }
   699  
   700  func (s *Scheme) Name() string {
   701  	return s.schemeName
   702  }
   703  
   704  // internalPackages are packages that ignored when creating a default reflector name. These packages are in the common
   705  // call chains to NewReflector, so they'd be low entropy names for reflectors
   706  var internalPackages = []string{"k8s.io/apimachinery/pkg/runtime/scheme.go"}
   707  

View as plain text