2 Copyright 2014 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
25 "k8s.io/apimachinery/pkg/conversion"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/apimachinery/pkg/util/naming"
28 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
29 "k8s.io/apimachinery/pkg/util/sets"
32 // Scheme defines methods for serializing and deserializing API objects, a type
33 // registry for converting group, version, and kind information to and from Go
34 // schemas, and mappings between Go schemas of different versions. A scheme is the
35 // foundation for a versioned API and versioned configuration over time.
37 // In a Scheme, a Type is a particular Go struct, a Version is a point-in-time
38 // identifier for a particular representation of that Type (typically backwards
39 // compatible), a Kind is the unique name for that Type within the Version, and a
40 // Group identifies a set of Versions, Kinds, and Types that evolve over time. An
41 // Unversioned Type is one that is not yet formally bound to a type and is promised
42 // to be backwards compatible (effectively a "v1" of a Type that does not expect
43 // to break in the future).
45 // Schemes are not expected to change at runtime and are only threadsafe after
46 // registration is complete.
48 // versionMap allows one to figure out the go type of an object with
49 // the given version and name.
50 gvkToType map[schema.GroupVersionKind]reflect.Type
52 // typeToGroupVersion allows one to find metadata for a given go object.
53 // The reflect.Type we index by should *not* be a pointer.
54 typeToGVK map[reflect.Type][]schema.GroupVersionKind
56 // unversionedTypes are transformed without conversion in ConvertToVersion.
57 unversionedTypes map[reflect.Type]schema.GroupVersionKind
59 // unversionedKinds are the names of kinds that can be created in the context of any group
61 // TODO: resolve the status of unversioned types.
62 unversionedKinds map[string]reflect.Type
64 // Map from version and resource to the corresponding func to convert
65 // resource field labels in that version to internal version.
66 fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
68 // defaulterFuncs is an array of interfaces to be called with an object to provide defaulting
69 // the provided object must be a pointer.
70 defaulterFuncs map[reflect.Type]func(interface{})
72 // converter stores all registered conversion functions. It also has
73 // default converting behavior.
74 converter *conversion.Converter
76 // versionPriority is a map of groups to ordered lists of versions for those groups indicating the
77 // default priorities of these versions as registered in the scheme
78 versionPriority map[string][]string
80 // observedVersions keeps track of the order we've seen versions during type registration
81 observedVersions []schema.GroupVersion
83 // schemeName is the name of this scheme. If you don't specify a name, the stack of the NewScheme caller will be used.
84 // This is useful for error reporting to indicate the origin of the scheme.
88 // FieldLabelConversionFunc converts a field selector to internal representation.
89 type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
91 // NewScheme creates a new Scheme. This scheme is pluggable by default.
92 func NewScheme() *Scheme {
94 gvkToType: map[schema.GroupVersionKind]reflect.Type{},
95 typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},
96 unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},
97 unversionedKinds: map[string]reflect.Type{},
98 fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},
99 defaulterFuncs: map[reflect.Type]func(interface{}){},
100 versionPriority: map[string][]string{},
101 schemeName: naming.GetNameFromCallsite(internalPackages...),
103 s.converter = conversion.NewConverter(s.nameFunc)
105 utilruntime.Must(s.AddConversionFuncs(DefaultEmbeddedConversions()...))
107 // Enable map[string][]string conversions by default
108 utilruntime.Must(s.AddConversionFuncs(DefaultStringConversions...))
109 utilruntime.Must(s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
110 utilruntime.Must(s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
114 // nameFunc returns the name of the type that we wish to use to determine when two types attempt
115 // a conversion. Defaults to the go name of the type if the type is not registered.
116 func (s *Scheme) nameFunc(t reflect.Type) string {
117 // find the preferred names for this type
118 gvks, ok := s.typeToGVK[t]
123 for _, gvk := range gvks {
124 internalGV := gvk.GroupVersion()
125 internalGV.Version = APIVersionInternal // this is hacky and maybe should be passed in
126 internalGVK := internalGV.WithKind(gvk.Kind)
128 if internalType, exists := s.gvkToType[internalGVK]; exists {
129 return s.typeToGVK[internalType][0].Kind
136 // fromScope gets the input version, desired output version, and desired Scheme
137 // from a conversion.Scope.
138 func (s *Scheme) fromScope(scope conversion.Scope) *Scheme {
142 // Converter allows access to the converter for the scheme
143 func (s *Scheme) Converter() *conversion.Converter {
147 // AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
148 // Whenever an object of this type is serialized, it is serialized with the provided group version and is not
149 // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an
150 // API group and version that would never be updated.
152 // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into
153 // every version with particular schemas. Resolve this method at that point.
154 func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
155 s.addObservedVersion(version)
156 s.AddKnownTypes(version, types...)
157 for _, obj := range types {
158 t := reflect.TypeOf(obj).Elem()
159 gvk := version.WithKind(t.Name())
160 s.unversionedTypes[t] = gvk
161 if old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {
162 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))
164 s.unversionedKinds[gvk.Kind] = t
168 // AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
169 // All objects passed to types should be pointers to structs. The name that go reports for
170 // the struct becomes the "kind" field when encoding. Version may not be empty - use the
171 // APIVersionInternal constant if you have a type that does not have a formal version.
172 func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
173 s.addObservedVersion(gv)
174 for _, obj := range types {
175 t := reflect.TypeOf(obj)
176 if t.Kind() != reflect.Ptr {
177 panic("All types must be pointers to structs.")
180 s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
184 // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
185 // be encoded as. Useful for testing when you don't want to make multiple packages to define
186 // your structs. Version may not be empty - use the APIVersionInternal constant if you have a
187 // type that does not have a formal version.
188 func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
189 s.addObservedVersion(gvk.GroupVersion())
190 t := reflect.TypeOf(obj)
191 if len(gvk.Version) == 0 {
192 panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
194 if t.Kind() != reflect.Ptr {
195 panic("All types must be pointers to structs.")
198 if t.Kind() != reflect.Struct {
199 panic("All types must be pointers to structs.")
202 if oldT, found := s.gvkToType[gvk]; found && oldT != t {
203 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))
208 for _, existingGvk := range s.typeToGVK[t] {
209 if existingGvk == gvk {
213 s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
216 // KnownTypes returns the types known for the given version.
217 func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {
218 types := make(map[string]reflect.Type)
219 for gvk, t := range s.gvkToType {
220 if gv != gvk.GroupVersion() {
229 // AllKnownTypes returns the all known types.
230 func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
234 // ObjectKinds returns all possible group,version,kind of the go object, true if the
235 // object is considered unversioned, or an error if it's not a pointer or is unregistered.
236 func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
237 // Unstructured objects are always considered to have their declared GVK
238 if _, ok := obj.(Unstructured); ok {
239 // we require that the GVK be populated in order to recognize the object
240 gvk := obj.GetObjectKind().GroupVersionKind()
241 if len(gvk.Kind) == 0 {
242 return nil, false, NewMissingKindErr("unstructured object has no kind")
244 if len(gvk.Version) == 0 {
245 return nil, false, NewMissingVersionErr("unstructured object has no version")
247 return []schema.GroupVersionKind{gvk}, false, nil
250 v, err := conversion.EnforcePtr(obj)
252 return nil, false, err
256 gvks, ok := s.typeToGVK[t]
258 return nil, false, NewNotRegisteredErrForType(s.schemeName, t)
260 _, unversionedType := s.unversionedTypes[t]
262 return gvks, unversionedType, nil
265 // Recognizes returns true if the scheme is able to handle the provided group,version,kind
267 func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {
268 _, exists := s.gvkToType[gvk]
272 func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
273 v, err := conversion.EnforcePtr(obj)
279 if _, ok := s.typeToGVK[t]; !ok {
282 _, ok := s.unversionedTypes[t]
286 // New returns a new API object of the given version and name, or an error if it hasn't
287 // been registered. The version and kind fields must be specified.
288 func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
289 if t, exists := s.gvkToType[kind]; exists {
290 return reflect.New(t).Interface().(Object), nil
293 if t, exists := s.unversionedKinds[kind.Kind]; exists {
294 return reflect.New(t).Interface().(Object), nil
296 return nil, NewNotRegisteredErrForKind(s.schemeName, kind)
299 // Log sets a logger on the scheme. For test purposes only
300 func (s *Scheme) Log(l conversion.DebugLogger) {
301 s.converter.Debug = l
304 // AddIgnoredConversionType identifies a pair of types that should be skipped by
305 // conversion (because the data inside them is explicitly dropped during
307 func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
308 return s.converter.RegisterIgnoredConversion(from, to)
311 // AddConversionFuncs adds functions to the list of conversion functions. The given
312 // functions should know how to convert between two of your API objects, or their
313 // sub-objects. We deduce how to call these functions from the types of their two
314 // parameters; see the comment for Converter.Register.
316 // Note that, if you need to copy sub-objects that didn't change, you can use the
317 // conversion.Scope object that will be passed to your conversion function.
318 // Additionally, all conversions started by Scheme will set the SrcVersion and
319 // DestVersion fields on the Meta object. Example:
321 // s.AddConversionFuncs(
322 // func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
323 // // You can depend on Meta() being non-nil, and this being set to
324 // // the source version, e.g., ""
325 // s.Meta().SrcVersion
326 // // You can depend on this being set to the destination version,
328 // s.Meta().DestVersion
329 // // Call scope.Convert to copy sub-fields.
330 // s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
335 // (For more detail about conversion functions, see Converter.Register's comment.)
337 // Also note that the default behavior, if you don't add a conversion function, is to
338 // sanely copy fields that have the same names and same type names. It's OK if the
339 // destination type has extra fields, but it must not remove any. So you only need to
340 // add conversion functions for things with changed/removed fields.
341 func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
342 for _, f := range conversionFuncs {
343 if err := s.converter.RegisterConversionFunc(f); err != nil {
350 // AddConversionFunc registers a function that converts between a and b by passing objects of those
351 // types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
352 // any other guarantee.
353 func (s *Scheme) AddConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
354 return s.converter.RegisterUntypedConversionFunc(a, b, fn)
357 // AddGeneratedConversionFunc registers a function that converts between a and b by passing objects of those
358 // types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
359 // any other guarantee.
360 func (s *Scheme) AddGeneratedConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
361 return s.converter.RegisterGeneratedUntypedConversionFunc(a, b, fn)
364 // AddFieldLabelConversionFunc adds a conversion function to convert field selectors
365 // of the given kind from the given version to internal version representation.
366 func (s *Scheme) AddFieldLabelConversionFunc(gvk schema.GroupVersionKind, conversionFunc FieldLabelConversionFunc) error {
367 s.fieldLabelConversionFuncs[gvk] = conversionFunc
371 // RegisterInputDefaults sets the provided field mapping function and field matching
372 // as the defaults for the provided input type. The fn may be nil, in which case no
373 // mapping will happen by default. Use this method to register a mechanism for handling
374 // a specific input type in conversion, such as a map[string]string to structs.
375 func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappingFunc, defaultFlags conversion.FieldMatchingFlags) error {
376 return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
379 // AddTypeDefaultingFunc registers a function that is passed a pointer to an
380 // object and can default fields on the object. These functions will be invoked
381 // when Default() is called. The function will never be called unless the
382 // defaulted object matches srcType. If this function is invoked twice with the
383 // same srcType, the fn passed to the later call will be used instead.
384 func (s *Scheme) AddTypeDefaultingFunc(srcType Object, fn func(interface{})) {
385 s.defaulterFuncs[reflect.TypeOf(srcType)] = fn
388 // Default sets defaults on the provided Object.
389 func (s *Scheme) Default(src Object) {
390 if fn, ok := s.defaulterFuncs[reflect.TypeOf(src)]; ok {
395 // Convert will attempt to convert in into out. Both must be pointers. For easy
396 // testing of conversion functions. Returns an error if the conversion isn't
397 // possible. You can call this with types that haven't been registered (for example,
398 // a to test conversion of types that are nested within registered types). The
399 // context interface is passed to the convertor. Convert also supports Unstructured
400 // types and will convert them intelligently.
401 func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
402 unstructuredIn, okIn := in.(Unstructured)
403 unstructuredOut, okOut := out.(Unstructured)
406 // converting unstructured input to an unstructured output is a straight copy - unstructured
407 // is a "smart holder" and the contents are passed by reference between the two objects
408 unstructuredOut.SetUnstructuredContent(unstructuredIn.UnstructuredContent())
412 // if the output is an unstructured object, use the standard Go type to unstructured
413 // conversion. The object must not be internal.
414 obj, ok := in.(Object)
416 return fmt.Errorf("unable to convert object type %T to Unstructured, must be a runtime.Object", in)
418 gvks, unversioned, err := s.ObjectKinds(obj)
424 // if no conversion is necessary, convert immediately
425 if unversioned || gvk.Version != APIVersionInternal {
426 content, err := DefaultUnstructuredConverter.ToUnstructured(in)
430 unstructuredOut.SetUnstructuredContent(content)
431 unstructuredOut.GetObjectKind().SetGroupVersionKind(gvk)
435 // attempt to convert the object to an external version first.
436 target, ok := context.(GroupVersioner)
438 return fmt.Errorf("unable to convert the internal object type %T to Unstructured without providing a preferred version to convert to", in)
440 // Convert is implicitly unsafe, so we don't need to perform a safe conversion
441 versioned, err := s.UnsafeConvertToVersion(obj, target)
445 content, err := DefaultUnstructuredConverter.ToUnstructured(versioned)
449 unstructuredOut.SetUnstructuredContent(content)
453 // converting an unstructured object to any type is modeled by first converting
454 // the input to a versioned type, then running standard conversions
455 typed, err := s.unstructuredToTyped(unstructuredIn)
462 flags, meta := s.generateConvertMeta(in)
463 meta.Context = context
465 flags = conversion.AllowDifferentFieldTypeNames
467 return s.converter.Convert(in, out, flags, meta)
470 // ConvertFieldLabel alters the given field label and value for an kind field selector from
471 // versioned representation to an unversioned one or returns an error.
472 func (s *Scheme) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
473 conversionFunc, ok := s.fieldLabelConversionFuncs[gvk]
475 return DefaultMetaV1FieldSelectorConversion(label, value)
477 return conversionFunc(label, value)
480 // ConvertToVersion attempts to convert an input object to its matching Kind in another
481 // version within this scheme. Will return an error if the provided version does not
482 // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
483 // return an error if the conversion does not result in a valid Object being
484 // returned. Passes target down to the conversion methods as the Context on the scope.
485 func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
486 return s.convertToVersion(true, in, target)
489 // UnsafeConvertToVersion will convert in to the provided target if such a conversion is possible,
490 // but does not guarantee the output object does not share fields with the input object. It attempts to be as
491 // efficient as possible when doing conversion.
492 func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
493 return s.convertToVersion(false, in, target)
496 // convertToVersion handles conversion with an optional copy.
497 func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error) {
500 if u, ok := in.(Unstructured); ok {
501 typed, err := s.unstructuredToTyped(u)
507 // unstructuredToTyped returns an Object, which must be a pointer to a struct.
508 t = reflect.TypeOf(in).Elem()
511 // determine the incoming kinds with as few allocations as possible.
512 t = reflect.TypeOf(in)
513 if t.Kind() != reflect.Ptr {
514 return nil, fmt.Errorf("only pointer types may be converted: %v", t)
517 if t.Kind() != reflect.Struct {
518 return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
522 kinds, ok := s.typeToGVK[t]
523 if !ok || len(kinds) == 0 {
524 return nil, NewNotRegisteredErrForType(s.schemeName, t)
527 gvk, ok := target.KindForGroupVersionKinds(kinds)
529 // try to see if this type is listed as unversioned (for legacy support)
530 // TODO: when we move to server API versions, we should completely remove the unversioned concept
531 if unversionedKind, ok := s.unversionedTypes[t]; ok {
532 if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
533 return copyAndSetTargetKind(copy, in, gvk)
535 return copyAndSetTargetKind(copy, in, unversionedKind)
537 return nil, NewNotRegisteredErrForTarget(s.schemeName, t, target)
540 // target wants to use the existing type, set kind and return (no conversion necessary)
541 for _, kind := range kinds {
543 return copyAndSetTargetKind(copy, in, gvk)
547 // type is unversioned, no conversion necessary
548 if unversionedKind, ok := s.unversionedTypes[t]; ok {
549 if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
550 return copyAndSetTargetKind(copy, in, gvk)
552 return copyAndSetTargetKind(copy, in, unversionedKind)
555 out, err := s.New(gvk)
561 in = in.DeepCopyObject()
564 flags, meta := s.generateConvertMeta(in)
565 meta.Context = target
566 if err := s.converter.Convert(in, out, flags, meta); err != nil {
570 setTargetKind(out, gvk)
574 // unstructuredToTyped attempts to transform an unstructured object to a typed
575 // object if possible. It will return an error if conversion is not possible, or the versioned
576 // Go form of the object. Note that this conversion will lose fields.
577 func (s *Scheme) unstructuredToTyped(in Unstructured) (Object, error) {
578 // the type must be something we recognize
579 gvks, _, err := s.ObjectKinds(in)
583 typed, err := s.New(gvks[0])
587 if err := DefaultUnstructuredConverter.FromUnstructured(in.UnstructuredContent(), typed); err != nil {
588 return nil, fmt.Errorf("unable to convert unstructured object to %v: %v", gvks[0], err)
593 // generateConvertMeta constructs the meta value we pass to Convert.
594 func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
595 return s.converter.DefaultMeta(reflect.TypeOf(in))
598 // copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
599 func copyAndSetTargetKind(copy bool, obj Object, kind schema.GroupVersionKind) (Object, error) {
601 obj = obj.DeepCopyObject()
603 setTargetKind(obj, kind)
607 // setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
608 func setTargetKind(obj Object, kind schema.GroupVersionKind) {
609 if kind.Version == APIVersionInternal {
610 // internal is a special case
611 // TODO: look at removing the need to special case this
612 obj.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
615 obj.GetObjectKind().SetGroupVersionKind(kind)
618 // SetVersionPriority allows specifying a precise order of priority. All specified versions must be in the same group,
619 // and the specified order overwrites any previously specified order for this group
620 func (s *Scheme) SetVersionPriority(versions ...schema.GroupVersion) error {
621 groups := sets.String{}
623 for _, version := range versions {
624 if len(version.Version) == 0 || version.Version == APIVersionInternal {
625 return fmt.Errorf("internal versions cannot be prioritized: %v", version)
628 groups.Insert(version.Group)
629 order = append(order, version.Version)
631 if len(groups) != 1 {
632 return fmt.Errorf("must register versions for exactly one group: %v", strings.Join(groups.List(), ", "))
635 s.versionPriority[groups.List()[0]] = order
639 // PrioritizedVersionsForGroup returns versions for a single group in priority order
640 func (s *Scheme) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {
641 ret := []schema.GroupVersion{}
642 for _, version := range s.versionPriority[group] {
643 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
645 for _, observedVersion := range s.observedVersions {
646 if observedVersion.Group != group {
650 for _, existing := range ret {
651 if existing == observedVersion {
657 ret = append(ret, observedVersion)
664 // PrioritizedVersionsAllGroups returns all known versions in their priority order. Groups are random, but
665 // versions for a single group are prioritized
666 func (s *Scheme) PrioritizedVersionsAllGroups() []schema.GroupVersion {
667 ret := []schema.GroupVersion{}
668 for group, versions := range s.versionPriority {
669 for _, version := range versions {
670 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
673 for _, observedVersion := range s.observedVersions {
675 for _, existing := range ret {
676 if existing == observedVersion {
682 ret = append(ret, observedVersion)
688 // PreferredVersionAllGroups returns the most preferred version for every group.
689 // group ordering is random.
690 func (s *Scheme) PreferredVersionAllGroups() []schema.GroupVersion {
691 ret := []schema.GroupVersion{}
692 for group, versions := range s.versionPriority {
693 for _, version := range versions {
694 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
698 for _, observedVersion := range s.observedVersions {
700 for _, existing := range ret {
701 if existing.Group == observedVersion.Group {
707 ret = append(ret, observedVersion)
714 // IsGroupRegistered returns true if types for the group have been registered with the scheme
715 func (s *Scheme) IsGroupRegistered(group string) bool {
716 for _, observedVersion := range s.observedVersions {
717 if observedVersion.Group == group {
724 // IsVersionRegistered returns true if types for the version have been registered with the scheme
725 func (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {
726 for _, observedVersion := range s.observedVersions {
727 if observedVersion == version {
735 func (s *Scheme) addObservedVersion(version schema.GroupVersion) {
736 if len(version.Version) == 0 || version.Version == APIVersionInternal {
739 for _, observedVersion := range s.observedVersions {
740 if observedVersion == version {
745 s.observedVersions = append(s.observedVersions, version)
748 func (s *Scheme) Name() string {
752 // internalPackages are packages that ignored when creating a default reflector name. These packages are in the common
753 // call chains to NewReflector, so they'd be low entropy names for reflectors
754 var internalPackages = []string{"k8s.io/apimachinery/pkg/runtime/scheme.go"}