blob: 59163d777156138ab11e437ed572d1dd3df55669 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package runtime
18
19import (
20 "fmt"
21 "net/url"
22 "reflect"
23
24 "strings"
25
26 "k8s.io/apimachinery/pkg/conversion"
27 "k8s.io/apimachinery/pkg/runtime/schema"
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.
46type Scheme struct {
47 // versionMap 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 // typeToGroupVersion 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[string]map[string]FieldLabelConversionFunc
66
67 // defaulterFuncs is an array of interfaces 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 coverting 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
83// Function to convert a field selector to internal representation.
84type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
85
86// NewScheme creates a new Scheme. This scheme is pluggable by default.
87func NewScheme() *Scheme {
88 s := &Scheme{
89 gvkToType: map[schema.GroupVersionKind]reflect.Type{},
90 typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},
91 unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},
92 unversionedKinds: map[string]reflect.Type{},
93 fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{},
94 defaulterFuncs: map[reflect.Type]func(interface{}){},
95 versionPriority: map[string][]string{},
96 }
97 s.converter = conversion.NewConverter(s.nameFunc)
98
99 s.AddConversionFuncs(DefaultEmbeddedConversions()...)
100
101 // Enable map[string][]string conversions by default
102 if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil {
103 panic(err)
104 }
105 if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
106 panic(err)
107 }
108 if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
109 panic(err)
110 }
111 return s
112}
113
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.
116func (s *Scheme) nameFunc(t reflect.Type) string {
117 // find the preferred names for this type
118 gvks, ok := s.typeToGVK[t]
119 if !ok {
120 return t.Name()
121 }
122
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)
127
128 if internalType, exists := s.gvkToType[internalGVK]; exists {
129 return s.typeToGVK[internalType][0].Kind
130 }
131 }
132
133 return gvks[0].Kind
134}
135
136// fromScope gets the input version, desired output version, and desired Scheme
137// from a conversion.Scope.
138func (s *Scheme) fromScope(scope conversion.Scope) *Scheme {
139 return s
140}
141
142// Converter allows access to the converter for the scheme
143func (s *Scheme) Converter() *conversion.Converter {
144 return s.converter
145}
146
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.
151//
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.
154func (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", old.PkgPath(), old.Name(), gvk))
163 }
164 s.unversionedKinds[gvk.Kind] = t
165 }
166}
167
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.
172func (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.")
178 }
179 t = t.Elem()
180 s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
181 }
182}
183
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.
188func (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))
193 }
194 if t.Kind() != reflect.Ptr {
195 panic("All types must be pointers to structs.")
196 }
197 t = t.Elem()
198 if t.Kind() != reflect.Struct {
199 panic("All types must be pointers to structs.")
200 }
201
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", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name()))
204 }
205
206 s.gvkToType[gvk] = t
207
208 for _, existingGvk := range s.typeToGVK[t] {
209 if existingGvk == gvk {
210 return
211 }
212 }
213 s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
214}
215
216// KnownTypes returns the types known for the given version.
217func (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() {
221 continue
222 }
223
224 types[gvk.Kind] = t
225 }
226 return types
227}
228
229// AllKnownTypes returns the all known types.
230func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
231 return s.gvkToType
232}
233
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.
236func (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")
243 }
244 if len(gvk.Version) == 0 {
245 return nil, false, NewMissingVersionErr("unstructured object has no version")
246 }
247 return []schema.GroupVersionKind{gvk}, false, nil
248 }
249
250 v, err := conversion.EnforcePtr(obj)
251 if err != nil {
252 return nil, false, err
253 }
254 t := v.Type()
255
256 gvks, ok := s.typeToGVK[t]
257 if !ok {
258 return nil, false, NewNotRegisteredErrForType(t)
259 }
260 _, unversionedType := s.unversionedTypes[t]
261
262 return gvks, unversionedType, nil
263}
264
265// Recognizes returns true if the scheme is able to handle the provided group,version,kind
266// of an object.
267func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {
268 _, exists := s.gvkToType[gvk]
269 return exists
270}
271
272func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
273 v, err := conversion.EnforcePtr(obj)
274 if err != nil {
275 return false, false
276 }
277 t := v.Type()
278
279 if _, ok := s.typeToGVK[t]; !ok {
280 return false, false
281 }
282 _, ok := s.unversionedTypes[t]
283 return ok, true
284}
285
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.
288func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
289 if t, exists := s.gvkToType[kind]; exists {
290 return reflect.New(t).Interface().(Object), nil
291 }
292
293 if t, exists := s.unversionedKinds[kind.Kind]; exists {
294 return reflect.New(t).Interface().(Object), nil
295 }
296 return nil, NewNotRegisteredErrForKind(kind)
297}
298
299// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
300// (for two conversion types) to the converter. These functions are checked first during
301// a normal conversion, but are otherwise not called. Use AddConversionFuncs when registering
302// typed conversions.
303func (s *Scheme) AddGenericConversionFunc(fn conversion.GenericConversionFunc) {
304 s.converter.AddGenericConversionFunc(fn)
305}
306
307// Log sets a logger on the scheme. For test purposes only
308func (s *Scheme) Log(l conversion.DebugLogger) {
309 s.converter.Debug = l
310}
311
312// AddIgnoredConversionType identifies a pair of types that should be skipped by
313// conversion (because the data inside them is explicitly dropped during
314// conversion).
315func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
316 return s.converter.RegisterIgnoredConversion(from, to)
317}
318
319// AddConversionFuncs adds functions to the list of conversion functions. The given
320// functions should know how to convert between two of your API objects, or their
321// sub-objects. We deduce how to call these functions from the types of their two
322// parameters; see the comment for Converter.Register.
323//
324// Note that, if you need to copy sub-objects that didn't change, you can use the
325// conversion.Scope object that will be passed to your conversion function.
326// Additionally, all conversions started by Scheme will set the SrcVersion and
327// DestVersion fields on the Meta object. Example:
328//
329// s.AddConversionFuncs(
330// func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
331// // You can depend on Meta() being non-nil, and this being set to
332// // the source version, e.g., ""
333// s.Meta().SrcVersion
334// // You can depend on this being set to the destination version,
335// // e.g., "v1".
336// s.Meta().DestVersion
337// // Call scope.Convert to copy sub-fields.
338// s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
339// return nil
340// },
341// )
342//
343// (For more detail about conversion functions, see Converter.Register's comment.)
344//
345// Also note that the default behavior, if you don't add a conversion function, is to
346// sanely copy fields that have the same names and same type names. It's OK if the
347// destination type has extra fields, but it must not remove any. So you only need to
348// add conversion functions for things with changed/removed fields.
349func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
350 for _, f := range conversionFuncs {
351 if err := s.converter.RegisterConversionFunc(f); err != nil {
352 return err
353 }
354 }
355 return nil
356}
357
358// AddGeneratedConversionFuncs registers conversion functions that were
359// automatically generated.
360func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
361 for _, f := range conversionFuncs {
362 if err := s.converter.RegisterGeneratedConversionFunc(f); err != nil {
363 return err
364 }
365 }
366 return nil
367}
368
369// AddFieldLabelConversionFunc adds a conversion function to convert field selectors
370// of the given kind from the given version to internal version representation.
371func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFunc FieldLabelConversionFunc) error {
372 if s.fieldLabelConversionFuncs[version] == nil {
373 s.fieldLabelConversionFuncs[version] = map[string]FieldLabelConversionFunc{}
374 }
375
376 s.fieldLabelConversionFuncs[version][kind] = conversionFunc
377 return nil
378}
379
380// AddStructFieldConversion allows you to specify a mechanical copy for a moved
381// or renamed struct field without writing an entire conversion function. See
382// the comment in conversion.Converter.SetStructFieldCopy for parameter details.
383// Call as many times as needed, even on the same fields.
384func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
385 return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName)
386}
387
388// RegisterInputDefaults sets the provided field mapping function and field matching
389// as the defaults for the provided input type. The fn may be nil, in which case no
390// mapping will happen by default. Use this method to register a mechanism for handling
391// a specific input type in conversion, such as a map[string]string to structs.
392func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappingFunc, defaultFlags conversion.FieldMatchingFlags) error {
393 return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
394}
395
396// AddTypeDefaultingFuncs registers a function that is passed a pointer to an
397// object and can default fields on the object. These functions will be invoked
398// when Default() is called. The function will never be called unless the
399// defaulted object matches srcType. If this function is invoked twice with the
400// same srcType, the fn passed to the later call will be used instead.
401func (s *Scheme) AddTypeDefaultingFunc(srcType Object, fn func(interface{})) {
402 s.defaulterFuncs[reflect.TypeOf(srcType)] = fn
403}
404
405// Default sets defaults on the provided Object.
406func (s *Scheme) Default(src Object) {
407 if fn, ok := s.defaulterFuncs[reflect.TypeOf(src)]; ok {
408 fn(src)
409 }
410}
411
412// Convert will attempt to convert in into out. Both must be pointers. For easy
413// testing of conversion functions. Returns an error if the conversion isn't
414// possible. You can call this with types that haven't been registered (for example,
415// a to test conversion of types that are nested within registered types). The
416// context interface is passed to the convertor. Convert also supports Unstructured
417// types and will convert them intelligently.
418func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
419 unstructuredIn, okIn := in.(Unstructured)
420 unstructuredOut, okOut := out.(Unstructured)
421 switch {
422 case okIn && okOut:
423 // converting unstructured input to an unstructured output is a straight copy - unstructured
424 // is a "smart holder" and the contents are passed by reference between the two objects
425 unstructuredOut.SetUnstructuredContent(unstructuredIn.UnstructuredContent())
426 return nil
427
428 case okOut:
429 // if the output is an unstructured object, use the standard Go type to unstructured
430 // conversion. The object must not be internal.
431 obj, ok := in.(Object)
432 if !ok {
433 return fmt.Errorf("unable to convert object type %T to Unstructured, must be a runtime.Object", in)
434 }
435 gvks, unversioned, err := s.ObjectKinds(obj)
436 if err != nil {
437 return err
438 }
439 gvk := gvks[0]
440
441 // if no conversion is necessary, convert immediately
442 if unversioned || gvk.Version != APIVersionInternal {
443 content, err := DefaultUnstructuredConverter.ToUnstructured(in)
444 if err != nil {
445 return err
446 }
447 unstructuredOut.SetUnstructuredContent(content)
448 unstructuredOut.GetObjectKind().SetGroupVersionKind(gvk)
449 return nil
450 }
451
452 // attempt to convert the object to an external version first.
453 target, ok := context.(GroupVersioner)
454 if !ok {
455 return fmt.Errorf("unable to convert the internal object type %T to Unstructured without providing a preferred version to convert to", in)
456 }
457 // Convert is implicitly unsafe, so we don't need to perform a safe conversion
458 versioned, err := s.UnsafeConvertToVersion(obj, target)
459 if err != nil {
460 return err
461 }
462 content, err := DefaultUnstructuredConverter.ToUnstructured(versioned)
463 if err != nil {
464 return err
465 }
466 unstructuredOut.SetUnstructuredContent(content)
467 return nil
468
469 case okIn:
470 // converting an unstructured object to any type is modeled by first converting
471 // the input to a versioned type, then running standard conversions
472 typed, err := s.unstructuredToTyped(unstructuredIn)
473 if err != nil {
474 return err
475 }
476 in = typed
477 }
478
479 flags, meta := s.generateConvertMeta(in)
480 meta.Context = context
481 if flags == 0 {
482 flags = conversion.AllowDifferentFieldTypeNames
483 }
484 return s.converter.Convert(in, out, flags, meta)
485}
486
487// ConvertFieldLabel alters the given field label and value for an kind field selector from
488// versioned representation to an unversioned one or returns an error.
489func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
490 if s.fieldLabelConversionFuncs[version] == nil {
491 return DefaultMetaV1FieldSelectorConversion(label, value)
492 }
493 conversionFunc, ok := s.fieldLabelConversionFuncs[version][kind]
494 if !ok {
495 return DefaultMetaV1FieldSelectorConversion(label, value)
496 }
497 return conversionFunc(label, value)
498}
499
500// ConvertToVersion attempts to convert an input object to its matching Kind in another
501// version within this scheme. Will return an error if the provided version does not
502// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
503// return an error if the conversion does not result in a valid Object being
504// returned. Passes target down to the conversion methods as the Context on the scope.
505func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
506 return s.convertToVersion(true, in, target)
507}
508
509// UnsafeConvertToVersion will convert in to the provided target if such a conversion is possible,
510// but does not guarantee the output object does not share fields with the input object. It attempts to be as
511// efficient as possible when doing conversion.
512func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
513 return s.convertToVersion(false, in, target)
514}
515
516// convertToVersion handles conversion with an optional copy.
517func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error) {
518 var t reflect.Type
519
520 if u, ok := in.(Unstructured); ok {
521 typed, err := s.unstructuredToTyped(u)
522 if err != nil {
523 return nil, err
524 }
525
526 in = typed
527 // unstructuredToTyped returns an Object, which must be a pointer to a struct.
528 t = reflect.TypeOf(in).Elem()
529
530 } else {
531 // determine the incoming kinds with as few allocations as possible.
532 t = reflect.TypeOf(in)
533 if t.Kind() != reflect.Ptr {
534 return nil, fmt.Errorf("only pointer types may be converted: %v", t)
535 }
536 t = t.Elem()
537 if t.Kind() != reflect.Struct {
538 return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
539 }
540 }
541
542 kinds, ok := s.typeToGVK[t]
543 if !ok || len(kinds) == 0 {
544 return nil, NewNotRegisteredErrForType(t)
545 }
546
547 gvk, ok := target.KindForGroupVersionKinds(kinds)
548 if !ok {
549 // try to see if this type is listed as unversioned (for legacy support)
550 // TODO: when we move to server API versions, we should completely remove the unversioned concept
551 if unversionedKind, ok := s.unversionedTypes[t]; ok {
552 if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
553 return copyAndSetTargetKind(copy, in, gvk)
554 }
555 return copyAndSetTargetKind(copy, in, unversionedKind)
556 }
557 return nil, NewNotRegisteredErrForTarget(t, target)
558 }
559
560 // target wants to use the existing type, set kind and return (no conversion necessary)
561 for _, kind := range kinds {
562 if gvk == kind {
563 return copyAndSetTargetKind(copy, in, gvk)
564 }
565 }
566
567 // type is unversioned, no conversion necessary
568 if unversionedKind, ok := s.unversionedTypes[t]; ok {
569 if gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{unversionedKind}); ok {
570 return copyAndSetTargetKind(copy, in, gvk)
571 }
572 return copyAndSetTargetKind(copy, in, unversionedKind)
573 }
574
575 out, err := s.New(gvk)
576 if err != nil {
577 return nil, err
578 }
579
580 if copy {
581 in = in.DeepCopyObject()
582 }
583
584 flags, meta := s.generateConvertMeta(in)
585 meta.Context = target
586 if err := s.converter.Convert(in, out, flags, meta); err != nil {
587 return nil, err
588 }
589
590 setTargetKind(out, gvk)
591 return out, nil
592}
593
594// unstructuredToTyped attempts to transform an unstructured object to a typed
595// object if possible. It will return an error if conversion is not possible, or the versioned
596// Go form of the object. Note that this conversion will lose fields.
597func (s *Scheme) unstructuredToTyped(in Unstructured) (Object, error) {
598 // the type must be something we recognize
599 gvks, _, err := s.ObjectKinds(in)
600 if err != nil {
601 return nil, err
602 }
603 typed, err := s.New(gvks[0])
604 if err != nil {
605 return nil, err
606 }
607 if err := DefaultUnstructuredConverter.FromUnstructured(in.UnstructuredContent(), typed); err != nil {
608 return nil, fmt.Errorf("unable to convert unstructured object to %v: %v", gvks[0], err)
609 }
610 return typed, nil
611}
612
613// generateConvertMeta constructs the meta value we pass to Convert.
614func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
615 return s.converter.DefaultMeta(reflect.TypeOf(in))
616}
617
618// copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
619func copyAndSetTargetKind(copy bool, obj Object, kind schema.GroupVersionKind) (Object, error) {
620 if copy {
621 obj = obj.DeepCopyObject()
622 }
623 setTargetKind(obj, kind)
624 return obj, nil
625}
626
627// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
628func setTargetKind(obj Object, kind schema.GroupVersionKind) {
629 if kind.Version == APIVersionInternal {
630 // internal is a special case
631 // TODO: look at removing the need to special case this
632 obj.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
633 return
634 }
635 obj.GetObjectKind().SetGroupVersionKind(kind)
636}
637
638// SetVersionPriority allows specifying a precise order of priority. All specified versions must be in the same group,
639// and the specified order overwrites any previously specified order for this group
640func (s *Scheme) SetVersionPriority(versions ...schema.GroupVersion) error {
641 groups := sets.String{}
642 order := []string{}
643 for _, version := range versions {
644 if len(version.Version) == 0 || version.Version == APIVersionInternal {
645 return fmt.Errorf("internal versions cannot be prioritized: %v", version)
646 }
647
648 groups.Insert(version.Group)
649 order = append(order, version.Version)
650 }
651 if len(groups) != 1 {
652 return fmt.Errorf("must register versions for exactly one group: %v", strings.Join(groups.List(), ", "))
653 }
654
655 s.versionPriority[groups.List()[0]] = order
656 return nil
657}
658
659// PrioritizedVersionsForGroup returns versions for a single group in priority order
660func (s *Scheme) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {
661 ret := []schema.GroupVersion{}
662 for _, version := range s.versionPriority[group] {
663 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
664 }
665 for _, observedVersion := range s.observedVersions {
666 if observedVersion.Group != group {
667 continue
668 }
669 found := false
670 for _, existing := range ret {
671 if existing == observedVersion {
672 found = true
673 break
674 }
675 }
676 if !found {
677 ret = append(ret, observedVersion)
678 }
679 }
680
681 return ret
682}
683
684// PrioritizedVersionsAllGroups returns all known versions in their priority order. Groups are random, but
685// versions for a single group are prioritized
686func (s *Scheme) PrioritizedVersionsAllGroups() []schema.GroupVersion {
687 ret := []schema.GroupVersion{}
688 for group, versions := range s.versionPriority {
689 for _, version := range versions {
690 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
691 }
692 }
693 for _, observedVersion := range s.observedVersions {
694 found := false
695 for _, existing := range ret {
696 if existing == observedVersion {
697 found = true
698 break
699 }
700 }
701 if !found {
702 ret = append(ret, observedVersion)
703 }
704 }
705 return ret
706}
707
708// PreferredVersionAllGroups returns the most preferred version for every group.
709// group ordering is random.
710func (s *Scheme) PreferredVersionAllGroups() []schema.GroupVersion {
711 ret := []schema.GroupVersion{}
712 for group, versions := range s.versionPriority {
713 for _, version := range versions {
714 ret = append(ret, schema.GroupVersion{Group: group, Version: version})
715 break
716 }
717 }
718 for _, observedVersion := range s.observedVersions {
719 found := false
720 for _, existing := range ret {
721 if existing.Group == observedVersion.Group {
722 found = true
723 break
724 }
725 }
726 if !found {
727 ret = append(ret, observedVersion)
728 }
729 }
730
731 return ret
732}
733
734// IsGroupRegistered returns true if types for the group have been registered with the scheme
735func (s *Scheme) IsGroupRegistered(group string) bool {
736 for _, observedVersion := range s.observedVersions {
737 if observedVersion.Group == group {
738 return true
739 }
740 }
741 return false
742}
743
744// IsVersionRegistered returns true if types for the version have been registered with the scheme
745func (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {
746 for _, observedVersion := range s.observedVersions {
747 if observedVersion == version {
748 return true
749 }
750 }
751
752 return false
753}
754
755func (s *Scheme) addObservedVersion(version schema.GroupVersion) {
756 if len(version.Version) == 0 || version.Version == APIVersionInternal {
757 return
758 }
759 for _, observedVersion := range s.observedVersions {
760 if observedVersion == version {
761 return
762 }
763 }
764
765 s.observedVersions = append(s.observedVersions, version)
766}