1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 type Scheme struct {
47
48
49 gvkToType map[schema.GroupVersionKind]reflect.Type
50
51
52
53 typeToGVK map[reflect.Type][]schema.GroupVersionKind
54
55
56 unversionedTypes map[reflect.Type]schema.GroupVersionKind
57
58
59
60
61 unversionedKinds map[string]reflect.Type
62
63
64
65 fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
66
67
68
69 defaulterFuncs map[reflect.Type]func(interface{})
70
71
72
73 converter *conversion.Converter
74
75
76
77 versionPriority map[string][]string
78
79
80 observedVersions []schema.GroupVersion
81
82
83
84 schemeName string
85 }
86
87
88 type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
89
90
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
105 utilruntime.Must(RegisterEmbeddedConversions(s))
106 utilruntime.Must(RegisterStringConversions(s))
107 return s
108 }
109
110
111 func (s *Scheme) Converter() *conversion.Converter {
112 return s.converter
113 }
114
115
116
117
118
119
120
121
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
137
138
139
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
153
154
155
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
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
187 reflect.ValueOf(a).MethodByName("DeepCopyInto").Call([]reflect.Value{reflect.ValueOf(b)})
188
189 b.(Object).GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
190 return nil
191 }); err != nil {
192 panic(err)
193 }
194 }
195 }
196
197
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
211
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
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
237 func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
238 return s.gvkToType
239 }
240
241
242
243 func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
244
245 if _, ok := obj.(Unstructured); ok {
246
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
273
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
294
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
307
308
309 func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
310 return s.converter.RegisterIgnoredConversion(from, to)
311 }
312
313
314
315
316 func (s *Scheme) AddConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
317 return s.converter.RegisterUntypedConversionFunc(a, b, fn)
318 }
319
320
321
322
323 func (s *Scheme) AddGeneratedConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {
324 return s.converter.RegisterGeneratedUntypedConversionFunc(a, b, fn)
325 }
326
327
328
329 func (s *Scheme) AddFieldLabelConversionFunc(gvk schema.GroupVersionKind, conversionFunc FieldLabelConversionFunc) error {
330 s.fieldLabelConversionFuncs[gvk] = conversionFunc
331 return nil
332 }
333
334
335
336
337
338
339 func (s *Scheme) AddTypeDefaultingFunc(srcType Object, fn func(interface{})) {
340 s.defaulterFuncs[reflect.TypeOf(srcType)] = fn
341 }
342
343
344 func (s *Scheme) Default(src Object) {
345 if fn, ok := s.defaulterFuncs[reflect.TypeOf(src)]; ok {
346 fn(src)
347 }
348 }
349
350
351
352
353
354
355
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
362
363 unstructuredOut.SetUnstructuredContent(unstructuredIn.UnstructuredContent())
364 return nil
365
366 case okOut:
367
368
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
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
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
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
409
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
423
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
433
434
435
436
437 func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
438 return s.convertToVersion(true, in, target)
439 }
440
441
442
443
444 func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
445 return s.convertToVersion(false, in, target)
446 }
447
448
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
460 t = reflect.TypeOf(in).Elem()
461
462 } else {
463
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
482
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
493 for _, kind := range kinds {
494 if gvk == kind {
495 return copyAndSetTargetKind(copy, in, gvk)
496 }
497 }
498
499
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
527
528
529 func (s *Scheme) unstructuredToTyped(in Unstructured) (Object, error) {
530
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
546 func (s *Scheme) generateConvertMeta(in interface{}) *conversion.Meta {
547 return s.converter.DefaultMeta(reflect.TypeOf(in))
548 }
549
550
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
560 func setTargetKind(obj Object, kind schema.GroupVersionKind) {
561 if kind.Version == APIVersionInternal {
562
563
564 obj.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
565 return
566 }
567 obj.GetObjectKind().SetGroupVersionKind(kind)
568 }
569
570
571
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
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
617
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
641
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
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
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
705
706 var internalPackages = []string{"k8s.io/apimachinery/pkg/runtime/scheme.go"}
707
View as plain text